#![allow(clippy::missing_safety_doc)]
#![allow(dead_code)]
#![cfg(target_os = "linux")]
pub mod poll;
use core::ffi::{c_char, c_int, c_uint, c_void};
use core::ptr;
use bun_uws_sys::{InternalLoopData, Loop, PosixLoop, Timespec};
const ADDR_BITS: u32 = 49;
const WAKEUP_TAG: u16 = 0;
#[inline]
fn encode_tagged_ptr(ptr: *mut c_void, tag: u16) -> u64 {
let addr = ptr as usize as u64;
let addr_mask: u64 = (1u64 << ADDR_BITS) - 1;
(addr & addr_mask) | ((tag as u64) << ADDR_BITS)
}
#[allow(dead_code)]
pub type LoopCb = unsafe extern "C" fn(*mut Loop);
#[allow(dead_code)]
pub type LoopCtxCb = unsafe extern "C" fn(*mut c_void, *mut Loop);
#[allow(dead_code)]
pub type DeferCb = unsafe extern "C" fn(*mut c_void);
#[repr(C)]
struct BaoWakeupAsync {
fd: c_int,
cb: Option<unsafe extern "C" fn(*mut BaoWakeupAsync)>,
}
struct BaoLoopState {
loop_ptr: *mut PosixLoop,
epfd: c_int,
wakeup_async_ptr: *mut BaoWakeupAsync,
pending_wakeups: core::sync::atomic::AtomicU32,
deferred: std::collections::VecDeque<DeferredCall>,
pre_handlers: Vec<HandlerSlot>,
post_handlers: Vec<HandlerSlot>,
wakeup_cb: Option<LoopCb>,
pre_cb: Option<LoopCb>,
post_cb: Option<LoopCb>,
}
unsafe impl Send for BaoLoopState {}
impl Drop for BaoLoopState {
fn drop(&mut self) {
if !self.wakeup_async_ptr.is_null() {
let wakeup = unsafe { &*self.wakeup_async_ptr };
if wakeup.fd >= 0 {
unsafe {
libc::close(wakeup.fd);
}
}
unsafe {
drop(Box::from_raw(self.wakeup_async_ptr));
}
self.wakeup_async_ptr = ptr::null_mut();
}
if !self.loop_ptr.is_null() {
let internal = unsafe { &(*self.loop_ptr).internal_loop_data };
if !internal.recv_buf.is_null() {
unsafe {
libc::free(internal.recv_buf as *mut c_void);
}
}
if !internal.send_buf.is_null() {
unsafe {
libc::free(internal.send_buf as *mut c_void);
}
}
unsafe {
drop(Box::from_raw(self.loop_ptr));
}
self.loop_ptr = ptr::null_mut();
}
if self.epfd >= 0 {
unsafe {
libc::close(self.epfd);
}
self.epfd = -1;
}
}
}
#[derive(Clone, Copy)]
struct DeferredCall {
ctx: *mut c_void,
cb: DeferCb,
}
#[derive(Clone, Copy)]
struct HandlerSlot {
ctx: *mut c_void,
cb: LoopCtxCb,
}
thread_local! {
static BAO_LOOP: std::cell::RefCell<Option<BaoLoopState>> =
const { std::cell::RefCell::new(None) };
}
fn create_loop(
wakeup_cb: Option<LoopCb>,
pre_cb: Option<LoopCb>,
post_cb: Option<LoopCb>,
) -> *mut PosixLoop {
const RECV_BUF_LEN: usize = 524_288;
let recv_buf: *mut u8 = unsafe { libc::malloc(RECV_BUF_LEN) as *mut u8 };
assert!(
!recv_buf.is_null(),
"bao_uloop: libc::malloc(recv_buf) failed"
);
unsafe { ptr::write_bytes(recv_buf, 0, RECV_BUF_LEN) };
let send_buf: *mut u8 = unsafe { libc::malloc(RECV_BUF_LEN) as *mut u8 };
assert!(
!send_buf.is_null(),
"bao_uloop: libc::malloc(send_buf) failed"
);
unsafe { ptr::write_bytes(send_buf, 0, RECV_BUF_LEN) };
let epfd = unsafe { libc::epoll_create1(libc::EPOLL_CLOEXEC) };
assert!(epfd >= 0, "bao_uloop: epoll_create1 failed");
let wakeup_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
assert!(wakeup_fd >= 0, "bao_uloop: eventfd failed");
let wakeup_async = Box::into_raw(Box::new(BaoWakeupAsync {
fd: wakeup_fd,
cb: None,
}));
let mut wakeup_event: libc::epoll_event = unsafe { core::mem::zeroed() };
wakeup_event.events = libc::EPOLLIN as u32;
wakeup_event.u64 = encode_tagged_ptr(wakeup_async as *mut c_void, WAKEUP_TAG);
let ret = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, wakeup_fd, &mut wakeup_event) };
assert!(ret == 0, "bao_uloop: epoll_ctl ADD wakeup_fd failed");
let internal = InternalLoopData {
sweep_timer: ptr::null_mut(),
sweep_timer_count: 0,
wakeup_async: wakeup_async as *mut bun_uws_sys::internal_loop_data::us_internal_async,
head: ptr::null_mut(),
quic_head: ptr::null_mut(),
quic_next_tick_us: 0,
quic_timer: ptr::null_mut(),
iterator: ptr::null_mut(),
recv_buf,
send_buf,
ssl_data: ptr::null_mut(),
pre_cb,
post_cb,
closed_udp_head: ptr::null_mut(),
closed_head: ptr::null_mut(),
low_prio_head: ptr::null_mut(),
low_prio_budget: 0,
dns_ready_head: ptr::null_mut(),
closed_connecting_head: ptr::null_mut(),
mutex: 0,
parent_ptr: ptr::null_mut(),
parent_tag: 0 as c_char,
iteration_nr: 0,
jsc_vm: ptr::null(), tick_depth: 0,
};
let boxed: Box<PosixLoop> = Box::new(PosixLoop {
internal_loop_data: internal,
num_polls: 0,
num_ready_polls: 0,
current_ready_poll: 0,
fd: epfd,
active: 0,
pending_wakeups: 0,
ready_polls: [unsafe { core::mem::zeroed() }; 1024],
});
let loop_ptr: *mut PosixLoop = Box::into_raw(boxed);
BAO_LOOP.with(|cell| {
let mut slot = cell.borrow_mut();
if slot.is_some() {
panic!("bao_uloop: thread already has a BaoLoopState — call us_loop_free first");
}
*slot = Some(BaoLoopState {
loop_ptr,
epfd,
wakeup_async_ptr: wakeup_async,
pending_wakeups: core::sync::atomic::AtomicU32::new(0),
deferred: std::collections::VecDeque::new(),
pre_handlers: Vec::new(),
post_handlers: Vec::new(),
wakeup_cb,
pre_cb,
post_cb,
});
});
loop_ptr
}
fn with_matching_state<R>(loop_: *mut Loop, f: impl FnOnce(&mut BaoLoopState) -> R) -> Option<R> {
BAO_LOOP.with(|cell| {
let mut slot = cell.borrow_mut();
let state = slot.as_mut()?;
if !ptr::eq(state.loop_ptr, loop_) {
return None;
}
Some(f(state))
})
}
fn take_deferred(loop_: *mut Loop) -> Vec<DeferredCall> {
BAO_LOOP.with(|cell| {
let mut slot = cell.borrow_mut();
let Some(state) = slot.as_mut() else {
return Vec::new();
};
if !ptr::eq(state.loop_ptr, loop_) {
return Vec::new();
}
state.deferred.drain(..).collect()
})
}
fn snapshot_handlers(loop_: *mut Loop, which: HandlerKind) -> Vec<HandlerSlot> {
BAO_LOOP.with(|cell| {
let mut slot = cell.borrow_mut();
let Some(state) = slot.as_mut() else {
return Vec::new();
};
if !ptr::eq(state.loop_ptr, loop_) {
return Vec::new();
}
match which {
HandlerKind::Pre => state.pre_handlers.clone(),
HandlerKind::Post => state.post_handlers.clone(),
}
})
}
#[derive(Clone, Copy)]
enum HandlerKind {
Pre,
Post,
}
fn run_epoll(loop_: *mut Loop, pending: u32, timeout: *const Timespec) {
let timeout_ms: c_int = if pending > 0 || timeout.is_null() {
0
} else {
let ts: Timespec = unsafe { *timeout };
if ts.sec == 0 && ts.nsec == 0 {
0
} else {
let ms = ts.sec * 1000 + ts.nsec / 1_000_000;
ms.min(i32::MAX as i64) as c_int
}
};
let epfd = BAO_LOOP.with(|cell| {
let slot = cell.borrow();
slot.as_ref()
.filter(|s| ptr::eq(s.loop_ptr, loop_))
.map(|s| s.epfd)
});
let Some(epfd) = epfd else { return };
let loop_ptr: *mut PosixLoop = loop_;
let nfds =
unsafe { libc::epoll_wait(epfd, (*loop_ptr).ready_polls.as_mut_ptr(), 1024, timeout_ms) };
if nfds <= 0 {
return;
}
unsafe {
(*loop_ptr).num_ready_polls = nfds;
(*loop_ptr).current_ready_poll = 0;
}
let wakeup_async_raw =
unsafe { (*loop_ptr).internal_loop_data.wakeup_async as *mut BaoWakeupAsync };
for i in 0..nfds {
let event = unsafe { (*loop_ptr).ready_polls[i as usize] };
if event.u64 == encode_tagged_ptr(wakeup_async_raw as *mut c_void, WAKEUP_TAG) {
if !wakeup_async_raw.is_null() {
let fd = unsafe { (*wakeup_async_raw).fd };
let mut buf: u64 = 0;
unsafe {
libc::read(fd, &mut buf as *mut u64 as *mut c_void, 8);
}
if let Some(cb) = unsafe { (*wakeup_async_raw).cb } {
unsafe { cb(wakeup_async_raw) };
}
}
unsafe {
(*loop_ptr).ready_polls[i as usize].u64 = 0;
}
}
}
unsafe {
poll::dispatch_ready_polls(loop_);
}
}
fn bump_iteration_nr(loop_: *mut Loop) {
BAO_LOOP.with(|cell| {
let mut slot = cell.borrow_mut();
let Some(state) = slot.as_mut() else { return };
if !ptr::eq(state.loop_ptr, loop_) {
return;
}
let p = state.loop_ptr;
unsafe {
(*p).internal_loop_data.iteration_nr =
(*p).internal_loop_data.iteration_nr.wrapping_add(1);
}
});
}
unsafe extern "C" {
pub safe fn uws_get_loop() -> *mut Loop;
pub unsafe fn us_create_loop(
hint: *mut c_void,
wakeup_cb: Option<LoopCb>,
pre_cb: Option<LoopCb>,
post_cb: Option<LoopCb>,
ext_size: c_uint,
) -> *mut Loop;
pub unsafe fn us_loop_free(loop_: *mut Loop);
pub unsafe fn us_wakeup_loop(loop_: *mut Loop);
pub unsafe fn us_loop_run(loop_: *mut Loop);
pub unsafe fn uws_loop_defer(loop_: *mut Loop, ctx: *mut c_void, cb: DeferCb);
pub unsafe fn uws_loop_addPreHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
pub unsafe fn uws_loop_removePreHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
pub unsafe fn uws_loop_addPostHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
pub unsafe fn uws_loop_removePostHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn bao_loop_tick(loop_: *mut Loop, timeout: *const Timespec) {
let loop_ptr: *mut PosixLoop = loop_;
let pending = unsafe { (*loop_ptr).active };
let has_rust_state = BAO_LOOP.with(|cell| {
cell.borrow()
.as_ref()
.map(|s| ptr::eq(s.loop_ptr, loop_))
.unwrap_or(false)
});
if has_rust_state {
run_epoll(loop_, pending, timeout);
} else {
unsafe extern "C" {
fn us_loop_run_bun_tick(loop_: *mut Loop, timeout: *const Timespec);
}
unsafe { us_loop_run_bun_tick(loop_, timeout) };
}
}
use bun_uws_sys::socket_group::VTable;
use bun_uws_sys::{ConnectingSocket, SocketKind, us_bun_verify_error_t, us_socket_t};
#[inline]
unsafe fn dispatch_via_vtable<S, R>(
s: *mut c_void,
fallback: S,
call: impl FnOnce(&'static VTable, *mut us_socket_t) -> R,
) -> R
where
S: FnOnce() -> R,
{
let sock = s as *mut us_socket_t;
let sock_ref = unsafe { &mut *sock };
let kind = sock_ref.kind();
if kind == SocketKind::Invalid {
panic!("us_dispatch: socket kind is Invalid — uninitialized or corrupted socket");
}
let group = sock_ref.raw_group();
match group.vtable {
Some(vtable) => call(vtable, sock),
None => fallback(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_open(
s: *mut c_void,
is_client: c_int,
ip: *mut u8,
ip_length: c_int,
) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_open {
Some(cb) => cb(sock, is_client, ip, ip_length) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_data(
s: *mut c_void,
data: *mut u8,
length: c_int,
) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_data {
Some(cb) => cb(sock, data, length) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_fd(s: *mut c_void, fd: c_int) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_fd {
Some(cb) => cb(sock, fd) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_writable(s: *mut c_void) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_writable {
Some(cb) => cb(sock) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_close(
s: *mut c_void,
code: c_int,
reason: *mut c_void,
) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_close {
Some(cb) => cb(sock, code, reason) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_timeout(s: *mut c_void) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_timeout {
Some(cb) => cb(sock) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_long_timeout(s: *mut c_void) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_long_timeout {
Some(cb) => cb(sock) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_end(s: *mut c_void) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_end {
Some(cb) => cb(sock) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_connect_error(s: *mut c_void, code: c_int) -> *mut c_void {
unsafe {
dispatch_via_vtable(
s,
|| s,
|vt, sock| match vt.on_connect_error {
Some(cb) => cb(sock, code) as *mut c_void,
None => s,
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_connecting_error(c: *mut c_void, code: c_int) -> *mut c_void {
let conn = c as *mut ConnectingSocket;
let conn_ref = unsafe { &mut *conn };
let group_ptr = conn_ref.raw_group();
if group_ptr.is_null() {
return c;
}
let group = unsafe { &*group_ptr };
match group.vtable {
Some(vtable) => match vtable.on_connecting_error {
Some(cb) => unsafe { cb(conn, code) as *mut c_void },
None => c,
},
None => c,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_handshake(
s: *mut c_void,
success: c_int,
err: us_bun_verify_error_t,
) {
unsafe {
dispatch_via_vtable(
s,
|| {},
|vt, sock| {
if let Some(cb) = vt.on_handshake {
cb(sock, success, err, core::ptr::null_mut());
}
},
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn us_dispatch_ssl_raw_tap(
s: *mut c_void,
_data: *mut u8,
_length: c_int,
) -> *mut c_void {
s
}
mod addrinfo;
pub use addrinfo::Bun__addrinfo_cancel;
pub use addrinfo::Bun__addrinfo_freeRequest;
pub use addrinfo::Bun__addrinfo_get;
pub use addrinfo::Bun__addrinfo_getRequestResult;
pub use addrinfo::Bun__addrinfo_registerQuic;
pub use addrinfo::Bun__addrinfo_registerQuic2;
pub use addrinfo::Bun__addrinfo_set;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn Bun__internal_ensureDateHeaderTimerIsEnabled(_loop: *mut c_void) {}
#[unsafe(no_mangle)]
pub static mut Bun__Node__UseSystemCA: bool = true;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn BUN__warn__extra_ca_load_failed(
filename: *const c_char,
error_msg: *const c_char,
) {
let filename_str = if filename.is_null() {
"(unknown)".to_string()
} else {
unsafe { std::ffi::CStr::from_ptr(filename) }
.to_string_lossy()
.into_owned()
};
let error_str = if error_msg.is_null() {
"(unknown)".to_string()
} else {
unsafe { std::ffi::CStr::from_ptr(error_msg) }
.to_string_lossy()
.into_owned()
};
eprintln!("warn: ignoring extra certs from {filename_str}, load failed: {error_str}");
}
#[unsafe(no_mangle)]
pub extern "C" fn bun_ssl_ctx_cache_on_free(
_parent: *mut c_void,
_ptr: *mut c_void,
_ad: *mut c_void,
_index: c_int,
_argl: i64,
_argp: *mut c_void,
) {
}
#[inline(never)]
pub fn force_link() {
poll::force_link_poll();
let _ = us_dispatch_open as unsafe extern "C" fn(_, _, _, _) -> *mut c_void;
let _ = us_dispatch_data as unsafe extern "C" fn(_, _, _) -> *mut c_void;
let _ = us_dispatch_fd as unsafe extern "C" fn(_, _) -> *mut c_void;
let _ = us_dispatch_writable as unsafe extern "C" fn(_) -> *mut c_void;
let _ = us_dispatch_close as unsafe extern "C" fn(_, _, _) -> *mut c_void;
let _ = us_dispatch_timeout as unsafe extern "C" fn(_) -> *mut c_void;
let _ = us_dispatch_long_timeout as unsafe extern "C" fn(_) -> *mut c_void;
let _ = us_dispatch_end as unsafe extern "C" fn(_) -> *mut c_void;
let _ = us_dispatch_connect_error as unsafe extern "C" fn(_, _) -> *mut c_void;
let _ = us_dispatch_connecting_error as unsafe extern "C" fn(_, _) -> *mut c_void;
let _ = us_dispatch_handshake as unsafe extern "C" fn(_, _, _);
let _ = us_dispatch_ssl_raw_tap as unsafe extern "C" fn(_, _, _) -> *mut c_void;
let _ = Bun__addrinfo_get as unsafe extern "C" fn(_, _, _, _) -> c_int;
let _ = Bun__addrinfo_set as unsafe extern "C" fn(_, _) -> c_int;
let _ = Bun__addrinfo_cancel as unsafe extern "C" fn(_, _) -> c_int;
let _ = Bun__addrinfo_freeRequest as unsafe extern "C" fn(_, _);
let _ = Bun__addrinfo_getRequestResult as unsafe extern "C" fn(_) -> *mut c_void;
let _ = Bun__addrinfo_registerQuic as unsafe extern "C" fn(_, _);
let _ = Bun__addrinfo_registerQuic2
as unsafe extern "C" fn(_, _, Option<unsafe extern "C" fn(_)>) -> ();
let _ = Bun__internal_ensureDateHeaderTimerIsEnabled as unsafe extern "C" fn(_);
}
#[cfg(test)]
mod hangup_tests {
use super::*;
use bun_uws_sys::socket_group::VTable;
use bun_uws_sys::{
LIBUS_SOCKET_ALLOW_HALF_OPEN, ListenSocket, SocketGroup, SocketKind, us_socket_t,
};
use std::sync::atomic::{AtomicUsize, Ordering};
static OPEN_COUNT: AtomicUsize = AtomicUsize::new(0);
static DATA_COUNT: AtomicUsize = AtomicUsize::new(0);
static DATA_BYTES: AtomicUsize = AtomicUsize::new(0);
static END_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLOSE_COUNT: AtomicUsize = AtomicUsize::new(0);
unsafe extern "C" fn t_open(
s: *mut us_socket_t,
_is_client: c_int,
_ip: *mut u8,
_ip_len: c_int,
) -> *mut us_socket_t {
OPEN_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_data(
s: *mut us_socket_t,
_data: *mut u8,
length: c_int,
) -> *mut us_socket_t {
DATA_COUNT.fetch_add(1, Ordering::SeqCst);
DATA_BYTES.fetch_add(length as usize, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_end(s: *mut us_socket_t) -> *mut us_socket_t {
END_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_close(
s: *mut us_socket_t,
_code: c_int,
_reason: *mut c_void,
) -> *mut us_socket_t {
CLOSE_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
static VTABLE: VTable = VTable {
on_open: Some(t_open),
on_data: Some(t_data),
on_fd: None,
on_writable: None,
on_close: Some(t_close),
on_timeout: None,
on_long_timeout: None,
on_end: Some(t_end),
on_connect_error: None,
on_connecting_error: None,
on_handshake: None,
};
#[unsafe(no_mangle)]
unsafe extern "C" fn Bun__internal_dispatch_ready_poll(
_loop: *mut Loop,
_tagged_pointer: *mut c_void,
) {
}
unsafe extern "C" fn noop_cb(_loop: *mut Loop) {}
#[test]
fn unix_half_open_peer_close_ends_once_and_closes() {
unsafe extern "C" {
fn us_loop_run_bun_tick(loop_: *mut Loop, timeout: *const Timespec);
}
let _ = bun_lsquic_sys::force_link as *const () as usize;
let _ = bun_lsquic_sys::force_link_lshpack as *const () as usize;
let _ = bun_threading::Mutex::new as *const () as usize;
let _ = bun_analytics::is_enabled as *const () as usize;
let path = format!("/tmp/bao-uloop-hangup-test-{}.sock", std::process::id());
let mut path_bytes = path.clone().into_bytes();
path_bytes.push(0);
let _ = std::fs::remove_file(&path);
let loop_ = unsafe {
us_create_loop(
ptr::null_mut(),
Some(noop_cb),
Some(noop_cb),
Some(noop_cb),
0,
)
};
assert!(!loop_.is_null(), "us_create_loop failed");
let group: &'static mut SocketGroup = Box::leak(Box::new(SocketGroup::default()));
group.init(loop_, Some(&VTABLE), ptr::null_mut());
let mut err: c_int = 0;
let ls: *mut ListenSocket = group.listen_unix(
SocketKind::Dynamic,
None,
&path_bytes,
LIBUS_SOCKET_ALLOW_HALF_OPEN,
0,
&mut err,
);
assert!(!ls.is_null(), "listen_unix failed, err = {err}");
for c in [
&OPEN_COUNT,
&DATA_COUNT,
&DATA_BYTES,
&END_COUNT,
&CLOSE_COUNT,
] {
c.store(0, Ordering::SeqCst);
}
{
let mut client = std::os::unix::net::UnixStream::connect(&path).expect("connect");
std::io::Write::write_all(&mut client, b"hello").expect("write");
std::io::Write::flush(&mut client).expect("flush");
}
let zero = Timespec { sec: 0, nsec: 0 };
for _ in 0..200 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if CLOSE_COUNT.load(Ordering::SeqCst) == 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(OPEN_COUNT.load(Ordering::SeqCst), 1, "one accepted socket");
assert_eq!(DATA_COUNT.load(Ordering::SeqCst), 1, "payload drained once");
assert_eq!(
DATA_BYTES.load(Ordering::SeqCst),
5,
"all 5 bytes delivered"
);
assert_eq!(
END_COUNT.load(Ordering::SeqCst),
1,
"on_end must fire exactly once"
);
assert_eq!(
CLOSE_COUNT.load(Ordering::SeqCst),
1,
"hangup must close the half-open socket (pre-fix it stayed open)"
);
for _ in 0..50 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
std::thread::sleep(std::time::Duration::from_millis(2));
}
assert_eq!(
END_COUNT.load(Ordering::SeqCst),
1,
"EPOLLHUP re-fired after close — spin regression"
);
assert_eq!(CLOSE_COUNT.load(Ordering::SeqCst), 1);
unsafe {
(*ls).close();
us_loop_run_bun_tick(loop_, &zero);
SocketGroup::destroy(group);
us_loop_free(loop_);
}
let _ = std::fs::remove_file(&path);
}
}
#[cfg(test)]
mod paused_eof_tests {
use super::*;
use bun_uws_sys::socket_group::VTable;
use bun_uws_sys::{ListenSocket, SocketGroup, SocketKind, us_socket_t};
use std::sync::atomic::{AtomicUsize, Ordering};
static OPEN_COUNT: AtomicUsize = AtomicUsize::new(0);
static DATA_BYTES: AtomicUsize = AtomicUsize::new(0);
static END_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLOSE_COUNT: AtomicUsize = AtomicUsize::new(0);
static SOCKET_PTR: std::sync::atomic::AtomicPtr<us_socket_t> =
std::sync::atomic::AtomicPtr::new(ptr::null_mut());
unsafe extern "C" {
fn us_loop_run_bun_tick(loop_: *mut Loop, timeout: *const Timespec);
fn us_internal_socket_raw_shutdown(s: *mut us_socket_t);
fn us_socket_get_fd(s: *mut us_socket_t) -> c_int;
fn us_socket_pause(s: *mut us_socket_t);
fn us_socket_resume(s: *mut us_socket_t);
}
unsafe extern "C" fn t_open(
s: *mut us_socket_t,
_is_client: c_int,
_ip: *mut u8,
_ip_len: c_int,
) -> *mut us_socket_t {
OPEN_COUNT.fetch_add(1, Ordering::SeqCst);
SOCKET_PTR.store(s, Ordering::SeqCst);
unsafe {
let fd = us_socket_get_fd(s);
let sz: c_int = 4 * 1024 * 1024;
libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_RCVBUF,
&sz as *const _ as *const c_void,
core::mem::size_of::<c_int>() as u32,
);
}
s
}
unsafe extern "C" fn t_data(
s: *mut us_socket_t,
_data: *mut u8,
length: c_int,
) -> *mut us_socket_t {
let first = DATA_BYTES.fetch_add(length as usize, Ordering::SeqCst) == 0;
if first {
unsafe { us_socket_pause(s) };
}
s
}
unsafe extern "C" fn t_end(s: *mut us_socket_t) -> *mut us_socket_t {
END_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_close(
s: *mut us_socket_t,
_code: c_int,
_reason: *mut c_void,
) -> *mut us_socket_t {
CLOSE_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
static VTABLE: VTable = VTable {
on_open: Some(t_open),
on_data: Some(t_data),
on_fd: None,
on_writable: None,
on_close: Some(t_close),
on_timeout: None,
on_long_timeout: None,
on_end: Some(t_end),
on_connect_error: None,
on_connecting_error: None,
on_handshake: None,
};
unsafe extern "C" fn noop_cb(_loop: *mut Loop) {}
#[test]
fn shutdown_paused_socket_defers_eof_and_keeps_tail() {
let _ = bun_lsquic_sys::force_link as *const () as usize;
let _ = bun_lsquic_sys::force_link_lshpack as *const () as usize;
let _ = bun_threading::Mutex::new as *const () as usize;
let _ = bun_analytics::is_enabled as *const () as usize;
const TOTAL: usize = 1024 * 1024;
let path = format!("/tmp/bao-uloop-paused-eof-test-{}.sock", std::process::id());
let mut path_bytes = path.clone().into_bytes();
path_bytes.push(0);
let _ = std::fs::remove_file(&path);
let loop_ = unsafe {
us_create_loop(
ptr::null_mut(),
Some(noop_cb),
Some(noop_cb),
Some(noop_cb),
0,
)
};
assert!(!loop_.is_null(), "us_create_loop failed");
let group: &'static mut SocketGroup = Box::leak(Box::new(SocketGroup::default()));
group.init(loop_, Some(&VTABLE), ptr::null_mut());
let mut err: c_int = 0;
let ls: *mut ListenSocket = group.listen_unix(
SocketKind::Dynamic,
None,
&path_bytes,
0,
0,
&mut err,
);
assert!(!ls.is_null(), "listen_unix failed, err = {err}");
for c in [&OPEN_COUNT, &DATA_BYTES, &END_COUNT, &CLOSE_COUNT] {
c.store(0, Ordering::SeqCst);
}
let zero = Timespec { sec: 0, nsec: 0 };
let mut client = std::os::unix::net::UnixStream::connect(&path).expect("connect");
for _ in 0..200 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if OPEN_COUNT.load(Ordering::SeqCst) == 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(OPEN_COUNT.load(Ordering::SeqCst), 1, "accept never ran");
let sock = SOCKET_PTR.load(Ordering::SeqCst);
assert!(!sock.is_null());
unsafe { us_internal_socket_raw_shutdown(sock) };
let writer = std::thread::spawn(move || {
let sz: c_int = 4 * 1024 * 1024;
unsafe {
libc::setsockopt(
client.as_raw_fd(),
libc::SOL_SOCKET,
libc::SO_SNDBUF,
&sz as *const _ as *const c_void,
core::mem::size_of::<c_int>() as u32,
);
}
let payload = vec![b'x'; TOTAL];
let _ = std::io::Write::write_all(&mut client, &payload);
drop(client); });
for _ in 0..200 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if DATA_BYTES.load(Ordering::SeqCst) > 0 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert!(
DATA_BYTES.load(Ordering::SeqCst) > 0,
"first data chunk never arrived"
);
for _ in 0..50 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
std::thread::sleep(std::time::Duration::from_millis(2));
}
assert_eq!(
CLOSE_COUNT.load(Ordering::SeqCst),
0,
"eof closed a paused shut-down socket instead of deferring (tail lost)"
);
assert_eq!(END_COUNT.load(Ordering::SeqCst), 0);
assert!(
DATA_BYTES.load(Ordering::SeqCst) < TOTAL,
"paused socket must not have drained the whole stream"
);
unsafe { us_socket_resume(sock) };
for _ in 0..400 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if CLOSE_COUNT.load(Ordering::SeqCst) == 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
writer.join().expect("writer thread");
assert_eq!(
DATA_BYTES.load(Ordering::SeqCst),
TOTAL,
"stream truncated: paused shut-down socket lost part of the peer's data"
);
assert_eq!(
END_COUNT.load(Ordering::SeqCst),
0,
"shut-down sockets close without on_end"
);
assert_eq!(
CLOSE_COUNT.load(Ordering::SeqCst),
1,
"socket must close after the drain (recv()==0 → eof → clean close)"
);
unsafe {
(*ls).close();
us_loop_run_bun_tick(loop_, &zero);
SocketGroup::destroy(group);
us_loop_free(loop_);
}
let _ = std::fs::remove_file(&path);
}
use std::os::fd::AsRawFd;
}
#[cfg(test)]
mod write_rearm_paused_tests {
use super::*;
use bun_uws_sys::socket_group::VTable;
use bun_uws_sys::{ListenSocket, SocketGroup, SocketKind, us_socket_t};
use std::sync::atomic::{AtomicUsize, Ordering};
static OPEN_COUNT: AtomicUsize = AtomicUsize::new(0);
static DATA_BYTES: AtomicUsize = AtomicUsize::new(0);
static CLOSE_COUNT: AtomicUsize = AtomicUsize::new(0);
static SOCKET_PTR: std::sync::atomic::AtomicPtr<us_socket_t> =
std::sync::atomic::AtomicPtr::new(ptr::null_mut());
unsafe extern "C" {
fn us_loop_run_bun_tick(loop_: *mut Loop, timeout: *const Timespec);
fn us_socket_get_fd(s: *mut us_socket_t) -> c_int;
fn us_socket_write(s: *mut us_socket_t, data: *const u8, length: c_int) -> c_int;
fn us_socket_pause(s: *mut us_socket_t);
fn us_socket_resume(s: *mut us_socket_t);
}
unsafe extern "C" fn t_open(
s: *mut us_socket_t,
_is_client: c_int,
_ip: *mut u8,
_ip_len: c_int,
) -> *mut us_socket_t {
OPEN_COUNT.fetch_add(1, Ordering::SeqCst);
SOCKET_PTR.store(s, Ordering::SeqCst);
unsafe {
let fd = us_socket_get_fd(s);
let sz: c_int = 8 * 1024;
libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_SNDBUF,
&sz as *const _ as *const c_void,
core::mem::size_of::<c_int>() as u32,
);
}
s
}
unsafe extern "C" fn t_data(
s: *mut us_socket_t,
_data: *mut u8,
length: c_int,
) -> *mut us_socket_t {
let first = DATA_BYTES.fetch_add(length as usize, Ordering::SeqCst) == 0;
if first {
unsafe { us_socket_pause(s) };
}
s
}
unsafe extern "C" fn t_close(
s: *mut us_socket_t,
_code: c_int,
_reason: *mut c_void,
) -> *mut us_socket_t {
CLOSE_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
static VTABLE: VTable = VTable {
on_open: Some(t_open),
on_data: Some(t_data),
on_fd: None,
on_writable: None,
on_close: Some(t_close),
on_timeout: None,
on_long_timeout: None,
on_end: None,
on_connect_error: None,
on_connecting_error: None,
on_handshake: None,
};
unsafe extern "C" fn noop_cb(_loop: *mut Loop) {}
#[test]
fn failed_write_does_not_resume_paused_socket() {
let _ = bun_lsquic_sys::force_link as *const () as usize;
let _ = bun_lsquic_sys::force_link_lshpack as *const () as usize;
let _ = bun_threading::Mutex::new as *const () as usize;
let _ = bun_analytics::is_enabled as *const () as usize;
let path = format!("/tmp/bao-uloop-write-rearm-test-{}.sock", std::process::id());
let mut path_bytes = path.clone().into_bytes();
path_bytes.push(0);
let _ = std::fs::remove_file(&path);
let loop_ = unsafe {
us_create_loop(
ptr::null_mut(),
Some(noop_cb),
Some(noop_cb),
Some(noop_cb),
0,
)
};
assert!(!loop_.is_null(), "us_create_loop failed");
let group: &'static mut SocketGroup = Box::leak(Box::new(SocketGroup::default()));
group.init(loop_, Some(&VTABLE), ptr::null_mut());
let mut err: c_int = 0;
let ls: *mut ListenSocket = group.listen_unix(
SocketKind::Dynamic,
None,
&path_bytes,
0,
0,
&mut err,
);
assert!(!ls.is_null(), "listen_unix failed, err = {err}");
for c in [&OPEN_COUNT, &DATA_BYTES, &CLOSE_COUNT] {
c.store(0, Ordering::SeqCst);
}
let zero = Timespec { sec: 0, nsec: 0 };
let mut client = std::os::unix::net::UnixStream::connect(&path).expect("connect");
for _ in 0..200 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if OPEN_COUNT.load(Ordering::SeqCst) == 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(OPEN_COUNT.load(Ordering::SeqCst), 1, "accept never ran");
let sock = SOCKET_PTR.load(Ordering::SeqCst);
assert!(!sock.is_null());
let first_chunk = vec![b'a'; 64 * 1024];
std::io::Write::write_all(&mut client, &first_chunk).expect("first write");
for _ in 0..200 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if DATA_BYTES.load(Ordering::SeqCst) > 0 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
let paused_bytes = DATA_BYTES.load(Ordering::SeqCst);
assert!(paused_bytes > 0, "first data chunk never arrived");
let tail = vec![b'b'; 256 * 1024];
unsafe {
let sz: c_int = 4 * 1024 * 1024;
libc::setsockopt(
client.as_raw_fd(),
libc::SOL_SOCKET,
libc::SO_SNDBUF,
&sz as *const _ as *const c_void,
core::mem::size_of::<c_int>() as u32,
);
}
std::io::Write::write_all(&mut client, &tail).expect("tail write");
let out = vec![b'x'; 64 * 1024];
let mut backpressured = false;
for _ in 0..128 {
let written =
unsafe { us_socket_write(sock, out.as_ptr(), out.len() as c_int) };
if written < out.len() as c_int {
backpressured = true;
break;
}
}
assert!(
backpressured,
"never hit a partial write: backpressure path not exercised"
);
for _ in 0..100 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
std::thread::sleep(std::time::Duration::from_millis(2));
}
assert_eq!(
DATA_BYTES.load(Ordering::SeqCst),
paused_bytes,
"failed write re-armed READABLE and undid the read pause"
);
assert_eq!(CLOSE_COUNT.load(Ordering::SeqCst), 0);
unsafe { us_socket_resume(sock) };
for _ in 0..400 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if DATA_BYTES.load(Ordering::SeqCst) >= first_chunk.len() + tail.len() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(
DATA_BYTES.load(Ordering::SeqCst),
first_chunk.len() + tail.len(),
"resume did not deliver the inbound tail"
);
drop(client);
for _ in 0..100 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if CLOSE_COUNT.load(Ordering::SeqCst) == 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(2));
}
unsafe {
(*ls).close();
us_loop_run_bun_tick(loop_, &zero);
SocketGroup::destroy(group);
us_loop_free(loop_);
}
let _ = std::fs::remove_file(&path);
}
use std::os::fd::AsRawFd;
}
#[cfg(test)]
mod ipc_recvmsg_tests {
use super::*;
use bun_uws_sys::socket_group::VTable;
use bun_uws_sys::{SocketGroup, SocketKind, us_socket_t};
use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
static OPEN_COUNT: AtomicUsize = AtomicUsize::new(0);
static FD_COUNT: AtomicUsize = AtomicUsize::new(0);
static RECEIVED_FD: AtomicI32 = AtomicI32::new(-1);
static DATA_BYTES: AtomicUsize = AtomicUsize::new(0);
static CLOSE_COUNT: AtomicUsize = AtomicUsize::new(0);
unsafe extern "C" {
fn us_loop_run_bun_tick(loop_: *mut Loop, timeout: *const Timespec);
fn us_socket_from_fd(
group: *mut SocketGroup,
kind: u8,
ssl_ctx: *mut c_void,
socket_ext_size: c_int,
fd: c_int,
ipc: c_int,
) -> *mut us_socket_t;
}
unsafe extern "C" fn t_open(
s: *mut us_socket_t,
_is_client: c_int,
_ip: *mut u8,
_ip_len: c_int,
) -> *mut us_socket_t {
OPEN_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_data(
s: *mut us_socket_t,
_data: *mut u8,
length: c_int,
) -> *mut us_socket_t {
DATA_BYTES.fetch_add(length as usize, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_fd(s: *mut us_socket_t, fd: c_int) -> *mut us_socket_t {
FD_COUNT.fetch_add(1, Ordering::SeqCst);
RECEIVED_FD.store(fd, Ordering::SeqCst);
s
}
unsafe extern "C" fn t_close(
s: *mut us_socket_t,
_code: c_int,
_reason: *mut c_void,
) -> *mut us_socket_t {
CLOSE_COUNT.fetch_add(1, Ordering::SeqCst);
s
}
static VTABLE: VTable = VTable {
on_open: Some(t_open),
on_data: Some(t_data),
on_fd: Some(t_fd),
on_writable: None,
on_close: Some(t_close),
on_timeout: None,
on_long_timeout: None,
on_end: None,
on_connect_error: None,
on_connecting_error: None,
on_handshake: None,
};
unsafe extern "C" fn noop_cb(_loop: *mut Loop) {}
fn count_fds_for_inode(inode: u64) -> usize {
let mut n = 0;
let dir = std::fs::read_dir("/proc/self/fd").expect("read /proc/self/fd");
for entry in dir.flatten() {
let path = entry.path();
if let Ok(fd_num) = path.file_name().unwrap().to_string_lossy().parse::<i32>() {
let mut st: libc::stat = unsafe { core::mem::zeroed() };
if unsafe { libc::fstat(fd_num, &mut st) } == 0 && st.st_ino == inode {
n += 1;
}
}
}
n
}
#[test]
fn ipc_recvmsg_delivers_fd_cloexec_and_closes_extras() {
let _ = bun_lsquic_sys::force_link as *const () as usize;
let _ = bun_lsquic_sys::force_link_lshpack as *const () as usize;
let _ = bun_threading::Mutex::new as *const () as usize;
let _ = bun_analytics::is_enabled as *const () as usize;
let mut sv: [c_int; 2] = [-1, -1];
assert_eq!(
unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, sv.as_mut_ptr()) },
0,
"socketpair failed"
);
let loop_ = unsafe {
us_create_loop(
ptr::null_mut(),
Some(noop_cb),
Some(noop_cb),
Some(noop_cb),
0,
)
};
assert!(!loop_.is_null(), "us_create_loop failed");
let group: &'static mut SocketGroup = Box::leak(Box::new(SocketGroup::default()));
group.init(loop_, Some(&VTABLE), ptr::null_mut());
let sock = unsafe {
us_socket_from_fd(
group,
SocketKind::Dynamic as u8,
ptr::null_mut(),
0,
sv[0],
1,
)
};
assert!(!sock.is_null(), "us_socket_from_fd failed");
for c in [&OPEN_COUNT, &FD_COUNT, &DATA_BYTES, &CLOSE_COUNT] {
c.store(0, Ordering::SeqCst);
}
RECEIVED_FD.store(-1, Ordering::SeqCst);
let mut pipe_fds: [c_int; 2] = [-1, -1];
assert_eq!(
unsafe { libc::pipe(pipe_fds.as_mut_ptr()) },
0,
"pipe failed"
);
let z = b'Z';
assert_eq!(
unsafe { libc::write(pipe_fds[1], &z as *const u8 as *const c_void, 1) },
1
);
let mut extra_pipe: [c_int; 2] = [-1, -1];
assert_eq!(
unsafe { libc::pipe(extra_pipe.as_mut_ptr()) },
0,
"extra pipe failed"
);
let extra_fd = extra_pipe[0];
let mut extra_st: libc::stat = unsafe { core::mem::zeroed() };
assert_eq!(unsafe { libc::fstat(extra_fd, &mut extra_st) }, 0);
let extra_ino = extra_st.st_ino;
#[repr(C, align(8))]
struct Control([u8; 64]);
let mut control = Control([0u8; 64]);
let nfds: usize = 2;
let sent = unsafe {
let cm = control.0.as_mut_ptr() as *mut libc::cmsghdr;
(*cm).cmsg_level = libc::SOL_SOCKET;
(*cm).cmsg_type = libc::SCM_RIGHTS;
(*cm).cmsg_len = libc::CMSG_LEN((nfds * core::mem::size_of::<c_int>()) as u32) as usize;
let data = libc::CMSG_DATA(cm) as *mut c_int;
*data = pipe_fds[0];
*data.add(1) = extra_fd;
let mut iov = libc::iovec {
iov_base: b"x".as_ptr() as *mut c_void,
iov_len: 1,
};
let mut msg: libc::msghdr = core::mem::zeroed();
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = control.0.as_mut_ptr() as *mut c_void;
msg.msg_controllen =
libc::CMSG_SPACE((nfds * core::mem::size_of::<c_int>()) as u32) as usize;
libc::sendmsg(sv[1], &msg, 0)
};
assert_eq!(sent, 1, "sendmsg failed");
let zero = Timespec { sec: 0, nsec: 0 };
for _ in 0..200 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if FD_COUNT.load(Ordering::SeqCst) == 1 && DATA_BYTES.load(Ordering::SeqCst) >= 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(FD_COUNT.load(Ordering::SeqCst), 1, "on_fd never fired");
assert_eq!(DATA_BYTES.load(Ordering::SeqCst), 1, "payload byte lost");
let received = RECEIVED_FD.load(Ordering::SeqCst);
assert!(received >= 0, "no fd delivered");
let mut buf: [u8; 1] = [0];
assert_eq!(
unsafe { libc::read(received, buf.as_mut_ptr() as *mut c_void, 1) },
1,
"delivered fd is not the pipe read end"
);
assert_eq!(buf[0], b'Z');
let flags = unsafe { libc::fcntl(received, libc::F_GETFD) };
assert!(
flags & libc::FD_CLOEXEC != 0,
"received fd lacks FD_CLOEXEC (MSG_CMSG_CLOEXEC not applied)"
);
unsafe {
libc::close(extra_fd);
libc::close(extra_pipe[1]);
}
assert_eq!(
count_fds_for_inode(extra_ino),
0,
"extra SCM_RIGHTS descriptor leaked (receiver must close beyond the first)"
);
unsafe {
libc::close(sv[1]);
}
for _ in 0..100 {
unsafe { us_loop_run_bun_tick(loop_, &zero) };
if CLOSE_COUNT.load(Ordering::SeqCst) == 1 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(2));
}
unsafe {
libc::close(received);
libc::close(pipe_fds[0]);
libc::close(pipe_fds[1]);
SocketGroup::destroy(group);
us_loop_free(loop_);
}
}
}