use core::ffi::{c_int, c_uint, c_void};
use crate::InternalLoopData;
use crate::Timespec;
#[cfg(windows)]
use bun_libuv_sys as uv;
bun_core::declare_scope!(Loop, visible);
#[repr(C, align(16))]
pub struct PosixLoop {
pub internal_loop_data: InternalLoopData,
pub num_polls: i32,
pub num_ready_polls: i32,
pub current_ready_poll: i32,
pub fd: i32,
pub active: u32,
pub pending_wakeups: u32,
pub ready_polls: [EventType; 1024],
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub type EventType = libc::epoll_event;
#[cfg(target_os = "macos")]
pub type EventType = libc::kevent64_s;
#[cfg(target_os = "freebsd")]
pub type EventType = libc::kevent;
#[cfg(windows)]
pub type EventType = *mut c_void;
pub trait LoopHandler {
const WAKEUP: unsafe extern "C" fn(*mut Loop);
const PRE: Option<unsafe extern "C" fn(*mut Loop)> = None;
const POST: Option<unsafe extern "C" fn(*mut Loop)> = None;
}
#[cfg(not(windows))]
impl PosixLoop {
pub fn uncork(&mut self) {
unsafe { c::uws_res_clear_corked_socket(self) };
}
pub fn update_date(&mut self) {
unsafe { c::uws_loop_date_header_timer_update(self) };
}
pub fn iteration_number(&self) -> u64 {
self.internal_loop_data.iteration_nr
}
#[inline]
pub fn current_ready_event(&self) -> EventType {
let idx = usize::try_from(self.current_ready_poll).expect("int cast");
self.ready_polls[idx]
}
pub fn inc(&mut self) {
bun_core::scoped_log!(Loop, "inc {} + 1 = {}", self.num_polls, self.num_polls + 1);
self.num_polls += 1;
}
pub fn dec(&mut self) {
bun_core::scoped_log!(Loop, "dec {} - 1 = {}", self.num_polls, self.num_polls - 1);
self.num_polls -= 1;
}
pub fn ref_(&mut self) {
bun_core::scoped_log!(
Loop,
"ref {} + 1 = {} | {} + 1 = {}",
self.num_polls,
self.num_polls + 1,
self.active,
self.active + 1
);
self.num_polls += 1;
self.active += 1;
}
pub fn unref(&mut self) {
bun_core::scoped_log!(
Loop,
"unref {} - 1 = {} | {} - 1 = {}",
self.num_polls,
self.num_polls - 1,
self.active,
self.active.saturating_sub(1)
);
self.num_polls -= 1;
self.active = self.active.saturating_sub(1);
}
pub fn is_active(&self) -> bool {
self.active > 0
}
pub fn add_active(&mut self, value: u32) {
bun_core::scoped_log!(
Loop,
"add {} + {} = {}",
self.active,
value,
self.active.saturating_add(value)
);
self.active = self.active.saturating_add(value);
}
pub fn sub_active(&mut self, value: u32) {
bun_core::scoped_log!(
Loop,
"sub {} - {} = {}",
self.active,
value,
self.active.saturating_sub(value)
);
self.active = self.active.saturating_sub(value);
}
pub fn unref_count(&mut self, count: i32) {
bun_core::scoped_log!(Loop, "unref x {}", count);
self.num_polls -= count;
self.active = self
.active
.saturating_sub(u32::try_from(count).expect("int cast"));
}
pub fn get() -> *mut Loop {
c::uws_get_loop()
}
pub fn drain_quic_if_necessary(&mut self) {
if self.internal_loop_data.quic_head.is_null() {
return;
}
unsafe { c::us_quic_loop_flush_if_pending(self) };
}
pub fn create<H: LoopHandler>() -> *mut Loop {
let p = unsafe {
c::us_create_loop(core::ptr::null_mut(), Some(H::WAKEUP), H::PRE, H::POST, 0)
};
assert!(!p.is_null(), "us_create_loop returned null");
p
}
pub fn wakeup(&mut self) {
unsafe { c::us_wakeup_loop(self) };
}
#[inline]
pub fn wake(&mut self) {
self.wakeup();
}
pub fn tick(&mut self) {
unsafe extern "C" { fn bao_loop_tick(loop_: *mut Loop, timeout: *const Timespec); }
unsafe { bao_loop_tick(self as *mut _, core::ptr::null()) };
}
pub fn tick_without_idle(&mut self) {
let timespec = Timespec { sec: 0, nsec: 0 };
unsafe extern "C" { fn bao_loop_tick(loop_: *mut Loop, timeout: *const Timespec); }
unsafe { bao_loop_tick(self as *mut _, &raw const timespec) };
}
pub fn tick_with_timeout(&mut self, timespec: Option<&Timespec>) {
unsafe extern "C" { fn bao_loop_tick(loop_: *mut Loop, timeout: *const Timespec); }
unsafe {
bao_loop_tick(self as *mut _, timespec.map_or(core::ptr::null(), std::ptr::from_ref))
};
}
pub fn drain_closed_sockets(&mut self) {
unsafe { c::us_internal_free_closed_sockets(self) };
}
pub fn close_all_groups(&mut self) -> bool {
unsafe { c::us_loop_close_all_groups(self) != 0 }
}
pub fn next_tick(
&mut self,
user_data: *mut c_void,
defer_callback: unsafe extern "C" fn(*mut c_void),
) {
unsafe { c::uws_loop_defer(self, user_data, defer_callback) };
}
pub unsafe fn add_post_handler(
this: *mut Self,
ctx: *mut c_void,
callback: unsafe extern "C" fn(*mut c_void, *mut Loop),
) -> Handler {
unsafe { c::uws_loop_addPostHandler(this, ctx, callback) };
Handler {
loop_: this,
ctx,
callback,
}
}
pub unsafe fn add_pre_handler(
this: *mut Self,
ctx: *mut c_void,
callback: unsafe extern "C" fn(*mut c_void, *mut Loop),
) -> Handler {
unsafe { c::uws_loop_addPreHandler(this, ctx, callback) };
Handler {
loop_: this,
ctx,
callback,
}
}
pub fn run(&mut self) {
unsafe { c::us_loop_run(self) };
}
pub fn should_enable_date_header_timer(&self) -> bool {
self.internal_loop_data.should_enable_date_header_timer()
}
pub unsafe fn destroy(this: *mut PosixLoop) {
unsafe { c::us_loop_free(this) };
}
}
pub struct Handler {
pub loop_: *mut Loop,
ctx: *mut c_void,
callback: unsafe extern "C" fn(*mut c_void, *mut Loop),
}
impl Handler {
pub fn remove_post(&self) {
unsafe { c::uws_loop_removePostHandler(self.loop_, self.ctx, self.callback) };
}
pub fn remove_pre(&self) {
unsafe { c::uws_loop_removePostHandler(self.loop_, self.ctx, self.callback) };
}
}
#[cfg(windows)]
#[repr(C, align(16))]
pub struct WindowsLoop {
pub internal_loop_data: InternalLoopData,
pub uv_loop: *mut uv::Loop,
pub is_default: c_int,
pub pre: *mut uv::uv_prepare_t,
pub check: *mut uv::uv_check_t,
}
#[cfg(windows)]
impl WindowsLoop {
pub fn should_enable_date_header_timer(&self) -> bool {
self.internal_loop_data.should_enable_date_header_timer()
}
pub fn uncork(&mut self) {
unsafe { c::uws_res_clear_corked_socket(self) };
}
pub fn get() -> *mut WindowsLoop {
unsafe { c::uws_get_loop_with_native(uv::Loop::get() as *mut c_void) }
}
pub fn iteration_number(&self) -> u64 {
self.internal_loop_data.iteration_nr
}
#[inline]
pub fn uv(&self) -> &uv::Loop {
unsafe { &*self.uv_loop }
}
#[inline]
fn uv_mut(&mut self) -> &mut uv::Loop {
unsafe { &mut *self.uv_loop }
}
pub fn add_active(&mut self, val: u32) {
self.uv_mut().add_active(val);
}
pub fn sub_active(&mut self, val: u32) {
self.uv_mut().sub_active(val);
}
pub fn is_active(&self) -> bool {
self.uv().is_active()
}
pub fn wakeup(&mut self) {
unsafe { c::us_wakeup_loop(self) };
}
#[inline]
pub fn wake(&mut self) {
self.wakeup();
}
pub fn tick_with_timeout(&mut self, _: Option<&Timespec>) {
unsafe { c::us_loop_run(self) };
}
pub fn tick_without_idle(&mut self) {
unsafe { c::us_loop_pump(self) };
}
pub fn drain_quic_if_necessary(&mut self) {
if self.internal_loop_data.quic_head.is_null() {
return;
}
unsafe { c::us_quic_loop_flush_if_pending(self) };
}
pub fn create<H: LoopHandler>() -> *mut WindowsLoop {
let p = unsafe {
c::us_create_loop(core::ptr::null_mut(), Some(H::WAKEUP), H::PRE, H::POST, 0)
};
assert!(!p.is_null(), "us_create_loop returned null");
p
}
pub fn run(&mut self) {
unsafe { c::us_loop_run(self) };
}
#[inline]
pub fn tick(&mut self) {
self.run();
}
#[inline]
pub fn wait(&mut self) {
self.run();
}
pub fn inc(&mut self) {
self.uv_mut().inc();
}
pub fn dec(&mut self) {
self.uv_mut().dec();
}
#[inline]
pub fn ref_(&mut self) {
self.inc();
}
#[inline]
pub fn unref(&mut self) {
self.dec();
}
pub fn drain_closed_sockets(&mut self) {
unsafe { c::us_internal_free_closed_sockets(self) };
}
pub fn close_all_groups(&mut self) -> bool {
unsafe { c::us_loop_close_all_groups(self) != 0 }
}
pub fn next_tick(
&mut self,
user_data: *mut c_void,
defer_callback: unsafe extern "C" fn(*mut c_void),
) {
unsafe { c::uws_loop_defer(self, user_data, defer_callback) };
}
pub fn update_date(&mut self) {
unsafe { c::uws_loop_date_header_timer_update(self) };
}
pub unsafe fn destroy(this: *mut WindowsLoop) {
unsafe { c::us_loop_free(this) };
}
pub unsafe fn add_post_handler(
this: *mut Self,
ctx: *mut c_void,
callback: unsafe extern "C" fn(*mut c_void, *mut Loop),
) -> Handler {
unsafe { c::uws_loop_addPostHandler(this, ctx, callback) };
Handler {
loop_: this,
ctx,
callback,
}
}
pub unsafe fn add_pre_handler(
this: *mut Self,
ctx: *mut c_void,
callback: unsafe extern "C" fn(*mut c_void, *mut Loop),
) -> Handler {
unsafe { c::uws_loop_addPreHandler(this, ctx, callback) };
Handler {
loop_: this,
ctx,
callback,
}
}
}
#[cfg(windows)]
pub type Loop = WindowsLoop;
#[cfg(not(windows))]
pub type Loop = PosixLoop;
pub(crate) type LoopCb = unsafe extern "C" fn(*mut Loop);
pub(crate) type LoopCtxCb = unsafe extern "C" fn(ctx: *mut c_void, loop_: *mut Loop);
pub(crate) type DeferCb = unsafe extern "C" fn(ctx: *mut c_void);
#[allow(non_snake_case)]
mod c {
use super::*;
unsafe extern "C" {
pub(super) 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(super) fn us_loop_free(loop_: *mut Loop);
pub(super) fn us_quic_loop_flush_if_pending(loop_: *mut Loop);
pub fn us_loop_run(loop_: *mut Loop);
#[cfg(windows)]
pub(super) fn us_loop_pump(loop_: *mut Loop);
pub fn us_wakeup_loop(loop_: *mut Loop);
pub(super) fn uws_loop_addPostHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
pub(super) fn uws_loop_removePostHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
pub(super) fn uws_loop_addPreHandler(loop_: *mut Loop, ctx: *mut c_void, cb: LoopCtxCb);
#[cfg(not(windows))]
#[allow(dead_code)]
pub(super) fn us_loop_run_bun_tick(loop_: *mut Loop, timeout_ms: *const Timespec);
pub(super) fn us_internal_free_closed_sockets(loop_: *mut Loop);
pub(super) fn us_loop_close_all_groups(loop_: *mut Loop) -> c_int;
#[cfg(not(windows))]
pub(super) safe fn uws_get_loop() -> *mut Loop;
#[cfg(windows)]
pub(super) fn uws_get_loop_with_native(native: *mut c_void) -> *mut WindowsLoop;
pub(super) fn uws_loop_defer(loop_: *mut Loop, ctx: *mut c_void, cb: DeferCb);
pub(super) fn uws_res_clear_corked_socket(loop_: *mut Loop);
pub(super) fn uws_loop_date_header_timer_update(loop_: *mut Loop);
}
}
pub use c::{us_loop_run, us_wakeup_loop};