#![allow(clippy::missing_safety_doc)]
use core::ffi::{c_int, c_uint, c_void};
use bun_uws_sys::{Loop, PosixLoop};
pub const POLL_TYPE_SOCKET: c_int = 0;
pub const POLL_TYPE_SOCKET_SHUT_DOWN: c_int = 1;
pub const POLL_TYPE_SEMI_SOCKET: c_int = 2;
pub const POLL_TYPE_CALLBACK: c_int = 3;
pub const POLL_TYPE_UDP: c_int = 4;
pub const POLL_TYPE_POLLING_OUT: c_int = 8;
pub const POLL_TYPE_POLLING_IN: c_int = 16;
pub const POLL_TYPE_KIND_MASK: c_int = 0b111;
pub const POLL_TYPE_POLLING_MASK: c_int = 0b11000;
#[repr(C, align(16))]
pub struct BaoPoll {
state: u32,
}
const FD_BITS: u32 = 27;
const FD_MASK: u32 = (1u32 << FD_BITS) - 1; const POLL_TYPE_SHIFT: u32 = FD_BITS;
const POLL_TYPE_MASK: u32 = !FD_MASK;
impl BaoPoll {
#[inline]
pub fn fd(&self) -> c_int {
let raw = (self.state & FD_MASK) as i32;
let shift = (32 - FD_BITS) as i32;
(raw << shift) >> shift
}
#[inline]
pub fn set_fd(&mut self, fd: c_int) {
self.state = (self.state & POLL_TYPE_MASK) | ((fd as u32) & FD_MASK);
}
#[inline]
pub fn poll_type(&self) -> c_int {
((self.state >> POLL_TYPE_SHIFT) as c_int) & 0x1F
}
#[inline]
pub fn set_poll_type(&mut self, pt: c_int) {
self.state = (self.state & FD_MASK) | (((pt as u32) & 0x1F) << POLL_TYPE_SHIFT);
}
#[inline]
pub fn kind(&self) -> c_int {
self.poll_type() & POLL_TYPE_KIND_MASK
}
#[inline]
pub fn events(&self) -> c_int {
let pt = self.poll_type();
(((pt & POLL_TYPE_POLLING_IN != 0) as c_int) * libc::EPOLLIN)
| (((pt & POLL_TYPE_POLLING_OUT != 0) as c_int) * libc::EPOLLOUT)
}
#[inline]
pub fn ext(&self) -> *mut c_void {
unsafe { (self as *const Self).add(1) as *mut c_void }
}
}
const UNSET_BITS_49_UNTIL_64: usize = 0x0000_FFFF_FFFF_FFFF;
#[inline]
fn clear_pointer_tag(p: *mut c_void) -> *mut c_void {
((p as usize) & UNSET_BITS_49_UNTIL_64) as *mut c_void
}
#[inline]
fn is_tagged_pointer(p: *mut c_void) -> bool {
clear_pointer_tag(p) != p
}
#[cfg(not(test))]
unsafe extern "C" {
fn Bun__internal_dispatch_ready_poll(loop_: *mut Loop, tagged_pointer: *mut c_void);
fn us_internal_dispatch_ready_poll(p: *mut c_void, error: c_int, eof: c_int, events: c_int);
}
#[cfg(test)]
#[allow(non_snake_case)]
unsafe extern "C" fn Bun__internal_dispatch_ready_poll(
_loop_: *mut Loop,
_tagged_pointer: *mut c_void,
) {
}
#[allow(dead_code)] 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;
fn us_dispatch_data(s: *mut c_void, data: *mut u8, length: c_int) -> *mut c_void;
fn us_dispatch_writable(s: *mut c_void) -> *mut c_void;
fn us_dispatch_close(s: *mut c_void, code: c_int, reason: *mut c_void) -> *mut c_void;
fn us_dispatch_end(s: *mut c_void) -> *mut c_void;
fn us_dispatch_connect_error(s: *mut c_void, code: c_int) -> *mut c_void;
}
pub type InternalCallbackFn = unsafe extern "C" fn(*mut BaoPoll);
#[repr(C)]
struct BaoInternalCallback {
p: BaoPoll,
loop_: *mut Loop,
cb_expects_the_loop: c_int,
leave_poll_ready: c_int,
cb: Option<InternalCallbackFn>,
has_added_timer_to_event_loop: c_uint,
}
#[inline]
unsafe fn dispatch_ready_poll(poll: *mut BaoPoll, error: c_int, eof: c_int, events: c_int) {
#[cfg(test)]
{
tests::DISPATCH_CALLS
.lock()
.unwrap()
.push((error, eof, events));
}
unsafe {
let kind = (*poll).kind();
match kind {
POLL_TYPE_CALLBACK => {
let cb_ptr = poll as *mut BaoInternalCallback;
if (*cb_ptr).leave_poll_ready == 0 {
accept_poll_event(poll);
}
if let Some(cb) = (*cb_ptr).cb {
if (*cb_ptr).cb_expects_the_loop != 0 {
cb((*cb_ptr).loop_ as *mut BaoPoll);
} else {
cb(poll);
}
}
}
POLL_TYPE_SEMI_SOCKET => {
}
POLL_TYPE_SOCKET | POLL_TYPE_SOCKET_SHUT_DOWN => {
let _ = (error, eof, events);
}
POLL_TYPE_UDP => {
}
_ => {}
}
}
}
unsafe fn accept_poll_event(poll: *mut BaoPoll) -> u64 {
let fd = unsafe { (*poll).fd() };
let mut buf: u64 = 0;
unsafe {
libc::read(fd, &mut buf as *mut u64 as *mut c_void, 8);
}
buf
}
unsafe extern "C" {
pub unsafe fn us_create_poll(
loop_: *mut Loop,
fallthrough: c_int,
ext_size: c_uint,
) -> *mut BaoPoll;
pub unsafe fn us_poll_free(p: *mut BaoPoll, loop_: *mut Loop);
pub unsafe fn us_poll_init(p: *mut BaoPoll, fd: c_int, poll_type: c_int);
pub unsafe fn us_poll_fd(p: *mut BaoPoll) -> c_int;
pub unsafe fn us_poll_events(p: *mut BaoPoll) -> c_int;
pub unsafe fn us_internal_poll_type(p: *mut BaoPoll) -> c_int;
pub unsafe fn us_internal_poll_set_type(p: *mut BaoPoll, poll_type: c_int);
pub unsafe fn us_poll_ext(p: *mut BaoPoll) -> *mut c_void;
pub unsafe fn us_poll_start(p: *mut BaoPoll, loop_: *mut Loop, events: c_int);
pub unsafe fn us_poll_start_rc(p: *mut BaoPoll, loop_: *mut Loop, events: c_int) -> c_int;
pub unsafe fn us_poll_change(p: *mut BaoPoll, loop_: *mut Loop, events: c_int) -> c_int;
pub unsafe fn us_poll_stop(p: *mut BaoPoll, loop_: *mut Loop);
pub unsafe fn us_poll_resize(
p: *mut BaoPoll,
loop_: *mut Loop,
old_ext_size: c_uint,
ext_size: c_uint,
) -> *mut BaoPoll;
pub unsafe fn us_internal_accept_poll_event(p: *mut BaoPoll) -> usize;
}
const LIBUS_POLL_EOF: c_int = 1;
const LIBUS_POLL_HANGUP: c_int = 2;
pub(crate) unsafe fn dispatch_ready_polls(loop_: *mut Loop) {
let loop_ptr: *mut PosixLoop = loop_;
let num_ready = unsafe { (*loop_ptr).num_ready_polls };
for i in 0..num_ready {
unsafe {
(*loop_ptr).current_ready_poll = i;
}
let event = unsafe { (*loop_ptr).ready_polls[i as usize] };
let poll_ptr = event.u64 as usize as *mut c_void;
if poll_ptr.is_null() {
continue;
}
if is_tagged_pointer(poll_ptr) {
unsafe {
Bun__internal_dispatch_ready_poll(loop_, poll_ptr);
}
continue;
}
let poll = poll_ptr;
let raw_events = event.events as c_int;
let error = ((raw_events & libc::EPOLLERR) != 0) as c_int;
let eof = if raw_events & libc::EPOLLHUP != 0 {
LIBUS_POLL_HANGUP
} else {
0
};
let events = raw_events & unsafe { us_poll_events(poll as *mut BaoPoll) };
if events != 0 || error != 0 || eof != 0 {
#[cfg(not(test))]
unsafe {
us_internal_dispatch_ready_poll(poll, error, eof, events);
}
#[cfg(test)]
unsafe {
dispatch_ready_poll(poll as *mut BaoPoll, error, eof, events);
}
}
}
}
#[inline(never)]
pub fn force_link_poll() {
}
#[cfg(test)]
mod tests {
use super::*;
use core::ptr;
pub(crate) static DISPATCH_CALLS: std::sync::Mutex<Vec<(c_int, c_int, c_int)>> =
std::sync::Mutex::new(Vec::new());
static DISPATCH_PROBE_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn bao_poll_layout_is_16_bytes_aligned() {
assert_eq!(core::mem::size_of::<BaoPoll>(), 16);
assert_eq!(core::mem::align_of::<BaoPoll>(), 16);
}
#[test]
fn bao_poll_fd_read_write() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_fd(42);
assert_eq!(p.fd(), 42);
p.set_fd(0);
assert_eq!(p.fd(), 0);
p.set_fd(-1);
assert_eq!(p.fd(), -1);
}
#[test]
fn bao_poll_fd_max_values() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_fd(67108863);
assert_eq!(p.fd(), 67108863);
p.set_fd(-67108864);
assert_eq!(p.fd(), -67108864);
}
#[test]
fn bao_poll_type_read_write() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(POLL_TYPE_SOCKET);
assert_eq!(p.poll_type(), POLL_TYPE_SOCKET);
assert_eq!(p.kind(), POLL_TYPE_SOCKET);
p.set_poll_type(POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_IN);
assert_eq!(p.poll_type(), POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_IN);
assert_eq!(p.kind(), POLL_TYPE_CALLBACK);
}
#[test]
fn bao_poll_all_kinds() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
for &kind in &[
POLL_TYPE_SOCKET,
POLL_TYPE_SOCKET_SHUT_DOWN,
POLL_TYPE_SEMI_SOCKET,
POLL_TYPE_CALLBACK,
POLL_TYPE_UDP,
] {
p.set_poll_type(kind);
assert_eq!(p.kind(), kind, "kind must match for {kind}");
}
}
#[test]
fn bao_poll_events_decoding() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_IN);
assert_eq!(p.events(), libc::EPOLLIN);
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_OUT);
assert_eq!(p.events(), libc::EPOLLOUT);
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_IN | POLL_TYPE_POLLING_OUT);
assert_eq!(p.events(), libc::EPOLLIN | libc::EPOLLOUT);
p.set_poll_type(POLL_TYPE_SOCKET);
assert_eq!(p.events(), 0);
}
#[test]
fn clear_pointer_tag_identifies_tagged() {
let tagged: *mut c_void = ((1usize << 49) | 0x1000) as *mut c_void;
assert!(is_tagged_pointer(tagged));
assert_eq!(clear_pointer_tag(tagged), 0x1000 as *mut c_void);
let normal: *mut c_void = 0x1000 as *mut c_void;
assert!(!is_tagged_pointer(normal));
assert_eq!(clear_pointer_tag(normal), normal);
}
#[test]
fn null_pointer_is_not_tagged() {
assert!(!is_tagged_pointer(ptr::null_mut()));
}
#[test]
fn clear_pointer_tag_is_idempotent() {
let tagged: *mut c_void = ((1usize << 49) | 0x1000) as *mut c_void;
let once = clear_pointer_tag(tagged);
let twice = clear_pointer_tag(once);
assert_eq!(once, twice);
}
#[test]
fn us_internal_poll_set_type_preserves_polling_bits() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_IN);
unsafe {
us_internal_poll_set_type(&mut p, POLL_TYPE_SOCKET_SHUT_DOWN);
}
assert_eq!(p.kind(), POLL_TYPE_SOCKET_SHUT_DOWN);
assert_eq!(p.poll_type() & POLL_TYPE_POLLING_MASK, POLL_TYPE_POLLING_IN);
}
#[test]
fn us_internal_poll_set_type_preserves_polling_out() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_OUT);
unsafe {
us_internal_poll_set_type(&mut p, POLL_TYPE_SOCKET);
}
assert_eq!(p.kind(), POLL_TYPE_SOCKET);
assert_eq!(
p.poll_type() & POLL_TYPE_POLLING_MASK,
POLL_TYPE_POLLING_OUT
);
}
#[test]
fn us_create_poll_returns_aligned_non_null() {
let loop_ = super::super::uws_get_loop();
let poll = unsafe { us_create_poll(loop_, 0, 0) };
assert!(!poll.is_null());
assert_eq!((poll as usize) % 16, 0, "poll must be 16-byte aligned");
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn us_create_poll_with_ext_size() {
let loop_ = super::super::uws_get_loop();
let ext = 64;
let poll = unsafe { us_create_poll(loop_, 0, ext) };
assert!(!poll.is_null());
let ext_ptr = unsafe { us_poll_ext(poll) };
assert!(!ext_ptr.is_null());
assert_eq!(ext_ptr as usize, poll as usize + 16);
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn us_create_poll_increments_num_polls() {
let loop_ = super::super::uws_get_loop();
let before = unsafe { (*loop_).num_polls };
let poll = unsafe { us_create_poll(loop_, 0, 0) };
let after = unsafe { (*loop_).num_polls };
assert_eq!(after, before + 1, "fallthrough=0 must increment num_polls");
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn us_create_poll_fallthrough_skips_increment() {
let loop_ = super::super::uws_get_loop();
let before = unsafe { (*loop_).num_polls };
let poll = unsafe { us_create_poll(loop_, 1, 0) };
let after = unsafe { (*loop_).num_polls };
assert_eq!(after, before, "fallthrough=1 must not increment num_polls");
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn us_poll_init_sets_fd_and_type() {
let loop_ = super::super::uws_get_loop();
let poll = unsafe { us_create_poll(loop_, 0, 0) };
unsafe {
us_poll_init(poll, 42, POLL_TYPE_CALLBACK);
}
assert_eq!(unsafe { us_poll_fd(poll) }, 42);
assert_eq!(unsafe { us_internal_poll_type(poll) }, POLL_TYPE_CALLBACK);
assert_eq!(
unsafe { us_poll_events(poll) },
0,
"no polling direction set"
);
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn poll_start_stop_cycle() {
let loop_ = super::super::uws_get_loop();
let epfd = unsafe { (*loop_).fd };
let mut fds: [c_int; 2] = [-1; 2];
let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(ret, 0, "pipe() failed");
let rfd = fds[0];
let poll = unsafe { us_create_poll(loop_, 0, 0) };
assert!(!poll.is_null());
unsafe {
us_poll_init(poll, rfd, POLL_TYPE_CALLBACK);
}
assert_eq!(unsafe { us_poll_fd(poll) }, rfd);
assert_eq!(unsafe { us_internal_poll_type(poll) }, POLL_TYPE_CALLBACK);
unsafe {
us_poll_start(poll, loop_, libc::EPOLLIN);
}
assert_eq!(unsafe { us_poll_events(poll) }, libc::EPOLLIN);
let mut ev: libc::epoll_event = unsafe { core::mem::zeroed() };
ev.events = libc::EPOLLIN as u32;
ev.u64 = 999; let rc = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, rfd, &mut ev) };
assert_eq!(rc, 0, "epoll_ctl MOD should succeed after poll_start");
unsafe {
us_poll_stop(poll, loop_);
}
let rc2 = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, rfd, &mut ev) };
assert!(rc2 != 0, "epoll_ctl MOD should fail after poll_stop");
unsafe {
us_poll_free(poll, loop_);
}
unsafe {
libc::close(fds[0]);
}
unsafe {
libc::close(fds[1]);
}
}
#[test]
fn poll_change_updates_events() {
let loop_ = super::super::uws_get_loop();
let mut fds: [c_int; 2] = [-1; 2];
let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(ret, 0);
let rfd = fds[0];
let poll = unsafe { us_create_poll(loop_, 0, 0) };
assert!(!poll.is_null());
unsafe {
us_poll_init(poll, rfd, POLL_TYPE_CALLBACK);
}
unsafe {
us_poll_start(poll, loop_, libc::EPOLLIN);
}
assert_eq!(unsafe { us_poll_events(poll) }, libc::EPOLLIN);
unsafe {
us_poll_change(poll, loop_, libc::EPOLLOUT);
}
assert_eq!(unsafe { us_poll_events(poll) }, libc::EPOLLOUT);
unsafe {
us_poll_change(poll, loop_, libc::EPOLLIN | libc::EPOLLOUT);
}
assert_eq!(
unsafe { us_poll_events(poll) },
libc::EPOLLIN | libc::EPOLLOUT
);
unsafe {
us_poll_stop(poll, loop_);
}
unsafe {
us_poll_free(poll, loop_);
}
unsafe {
libc::close(fds[0]);
}
unsafe {
libc::close(fds[1]);
}
}
#[test]
fn poll_start_rc_returns_zero_on_success() {
let loop_ = super::super::uws_get_loop();
let mut fds: [c_int; 2] = [-1; 2];
let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(ret, 0);
let rfd = fds[0];
let poll = unsafe { us_create_poll(loop_, 0, 0) };
unsafe {
us_poll_init(poll, rfd, POLL_TYPE_CALLBACK);
}
let rc = unsafe { us_poll_start_rc(poll, loop_, libc::EPOLLIN) };
assert_eq!(rc, 0, "us_poll_start_rc must return 0 on success");
unsafe {
us_poll_stop(poll, loop_);
}
unsafe {
us_poll_free(poll, loop_);
}
unsafe {
libc::close(fds[0]);
}
unsafe {
libc::close(fds[1]);
}
}
#[test]
fn poll_free_with_null_is_no_op() {
let loop_ = super::super::uws_get_loop();
unsafe {
us_poll_free(ptr::null_mut(), loop_);
}
}
#[test]
fn poll_resize_same_size_returns_same_pointer() {
let loop_ = super::super::uws_get_loop();
let poll = unsafe { us_create_poll(loop_, 0, 32) };
unsafe {
us_poll_init(poll, -1, POLL_TYPE_CALLBACK);
}
let new_p = unsafe { us_poll_resize(poll, loop_, 32, 32) };
assert_eq!(new_p, poll, "resize to same size must return same pointer");
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn poll_resize_larger_returns_new_pointer() {
let loop_ = super::super::uws_get_loop();
let poll = unsafe { us_create_poll(loop_, 0, 16) };
unsafe {
us_poll_init(poll, -1, POLL_TYPE_CALLBACK);
}
let new_p = unsafe { us_poll_resize(poll, loop_, 16, 64) };
assert!(!new_p.is_null(), "resize must return non-null");
unsafe {
us_poll_free(new_p, loop_);
}
}
#[test]
fn bao_poll_fd_and_poll_type_independent() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_fd(100);
p.set_poll_type(POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_IN);
assert_eq!(p.fd(), 100);
assert_eq!(p.poll_type(), POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_IN);
p.set_fd(200);
assert_eq!(p.fd(), 200);
assert_eq!(p.poll_type(), POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_IN);
p.set_poll_type(POLL_TYPE_SOCKET);
assert_eq!(p.fd(), 200);
assert_eq!(p.poll_type(), POLL_TYPE_SOCKET);
}
#[test]
fn bao_poll_fd_negative_values() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_fd(-1);
assert_eq!(p.fd(), -1);
p.set_fd(-100);
assert_eq!(p.fd(), -100);
p.set_fd(-67108864); assert_eq!(p.fd(), -67108864);
}
#[test]
fn bao_poll_poll_type_max_5bit() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(31);
assert_eq!(p.poll_type(), 31);
p.set_poll_type(32);
assert_eq!(p.poll_type(), 0, "32 & 0x1F = 0");
p.set_poll_type(33);
assert_eq!(p.poll_type(), 1, "33 & 0x1F = 1");
}
#[test]
fn bao_poll_kind_masks_low_3_bits() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(7);
assert_eq!(p.kind(), 7);
p.set_poll_type(POLL_TYPE_CALLBACK | POLL_TYPE_POLLING_IN);
assert_eq!(p.kind(), POLL_TYPE_CALLBACK);
}
#[test]
fn bao_poll_events_all_combinations() {
let mut p: BaoPoll = unsafe { core::mem::zeroed() };
p.set_poll_type(POLL_TYPE_SOCKET);
assert_eq!(p.events(), 0);
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_IN);
assert_eq!(p.events(), libc::EPOLLIN);
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_OUT);
assert_eq!(p.events(), libc::EPOLLOUT);
p.set_poll_type(POLL_TYPE_SOCKET | POLL_TYPE_POLLING_IN | POLL_TYPE_POLLING_OUT);
assert_eq!(p.events(), libc::EPOLLIN | libc::EPOLLOUT);
}
#[test]
fn bao_poll_ext_returns_pointer_after_header() {
let loop_ = super::super::uws_get_loop();
let poll = unsafe { us_create_poll(loop_, 0, 32) };
let ext = unsafe { us_poll_ext(poll) };
assert!(!ext.is_null());
assert_eq!(
ext as usize,
poll as usize + 16,
"ext must be 16 bytes after poll"
);
unsafe {
us_poll_free(poll, loop_);
}
}
#[test]
fn clear_pointer_tag_preserves_low_49_bits() {
let all_bits: *mut c_void = usize::MAX as *mut c_void;
let cleared = clear_pointer_tag(all_bits);
let expected: usize = 0x0000_FFFF_FFFF_FFFF;
assert_eq!(cleared as usize, expected);
}
#[test]
fn is_tagged_pointer_various_tags() {
let base: usize = 0x1000;
let tagged: *mut c_void = (base | (1usize << 49)) as *mut c_void;
assert!(is_tagged_pointer(tagged));
let high_tag: *mut c_void = (base | (1usize << 63)) as *mut c_void;
assert!(is_tagged_pointer(high_tag));
let plain: *mut c_void = base as *mut c_void;
assert!(!is_tagged_pointer(plain));
}
#[test]
fn clear_pointer_tag_roundtrip_with_re_encode() {
let ptr = 0x5000 as *mut c_void;
let tagged: *mut c_void = ((1usize << 49) | (ptr as usize)) as *mut c_void;
let cleared = clear_pointer_tag(tagged);
assert_eq!(cleared, ptr);
}
fn dispatch_once(raw_events: u32, poll: &mut BaoPoll) -> (c_int, c_int, c_int) {
let _probe_guard = DISPATCH_PROBE_LOCK.lock().unwrap();
let mut calls = DISPATCH_CALLS.lock().unwrap();
calls.clear();
drop(calls);
let boxed: *mut PosixLoop =
Box::into_raw(unsafe { Box::new(core::mem::zeroed::<PosixLoop>()) });
unsafe {
(*boxed).num_ready_polls = 1;
(*boxed).current_ready_poll = 0;
(*boxed).ready_polls[0].events = raw_events;
(*boxed).ready_polls[0].u64 = poll as *mut BaoPoll as usize as u64;
}
unsafe {
dispatch_ready_polls(boxed as *mut Loop);
}
drop(unsafe { Box::from_raw(boxed) });
let calls = DISPATCH_CALLS.lock().unwrap();
assert_eq!(calls.len(), 1, "exactly one dispatch expected");
calls[0]
}
#[test]
fn epollhup_is_tagged_as_libus_poll_hangup() {
let mut poll: BaoPoll = unsafe { core::mem::zeroed() };
poll.set_fd(7);
poll.set_poll_type(POLL_TYPE_UDP | POLL_TYPE_POLLING_IN | POLL_TYPE_POLLING_OUT);
let (error, eof, events) = dispatch_once(
(libc::EPOLLHUP | libc::EPOLLIN | libc::EPOLLOUT) as u32,
&mut poll,
);
assert_eq!(error, 0);
assert_eq!(eof, LIBUS_POLL_HANGUP);
assert_eq!(events, libc::EPOLLIN | libc::EPOLLOUT);
}
#[test]
fn epollerr_is_normalized_and_events_masked_to_armed_interest() {
let mut poll: BaoPoll = unsafe { core::mem::zeroed() };
poll.set_fd(9);
poll.set_poll_type(POLL_TYPE_UDP | POLL_TYPE_POLLING_IN);
let (error, eof, events) = dispatch_once(
(libc::EPOLLERR | libc::EPOLLHUP | libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT)
as u32,
&mut poll,
);
assert_eq!(error, 1);
assert_eq!(eof, LIBUS_POLL_HANGUP);
assert_eq!(events, libc::EPOLLIN);
}
#[test]
fn plain_epollin_drains_without_eof_marker() {
let mut poll: BaoPoll = unsafe { core::mem::zeroed() };
poll.set_fd(11);
poll.set_poll_type(POLL_TYPE_UDP | POLL_TYPE_POLLING_IN);
let (error, eof, events) = dispatch_once(libc::EPOLLIN as u32, &mut poll);
assert_eq!(error, 0);
assert_eq!(eof, 0);
assert_eq!(events, libc::EPOLLIN);
}
#[test]
fn rdhup_without_hup_is_not_a_hangup() {
let mut poll: BaoPoll = unsafe { core::mem::zeroed() };
poll.set_fd(12);
poll.set_poll_type(POLL_TYPE_UDP | POLL_TYPE_POLLING_IN | POLL_TYPE_POLLING_OUT);
let (error, eof, events) =
dispatch_once((libc::EPOLLRDHUP | libc::EPOLLIN) as u32, &mut poll);
assert_eq!(error, 0);
assert_eq!(eof, 0);
assert_eq!(events, libc::EPOLLIN);
}
}