use core::ptr::NonNull;
use bun_dotenv::Loader as DotEnvLoader;
use bun_io::FilePoll;
use bun_ptr::BackRef;
use bun_uws::Loop as UwsLoop;
use crate::AnyTaskWithExtraContext::AnyTaskWithExtraContext;
use crate::ConcurrentTask::ConcurrentTask;
use crate::MiniEventLoop::{EventLoopKind, MiniEventLoop};
use crate::{JsEventLoop, JsEventLoopKind};
unsafe extern "Rust" {
pub(crate) safe fn __bun_js_event_loop_current() -> *mut ();
}
#[inline]
fn jsc_event_loop_handle(js_event_loop: *mut ()) -> JsEventLoop {
unsafe { JsEventLoop::new(JsEventLoopKind::Jsc, js_event_loop) }
}
pub enum AnyEventLoop<'a> {
Js {
owner: JsEventLoop,
},
Mini(Box<MiniEventLoop<'a>>),
}
pub type Task = AnyTaskWithExtraContext;
impl<'a> Default for AnyEventLoop<'a> {
fn default() -> Self {
AnyEventLoop::Mini(Box::new(MiniEventLoop::init()))
}
}
impl<'a> AnyEventLoop<'a> {
pub fn iteration_number(&self) -> u64 {
match self {
AnyEventLoop::Js { owner } => owner.iteration_number(),
AnyEventLoop::Mini(mini) => unsafe { (*mini.loop_ptr()).iteration_number() },
}
}
#[inline]
pub fn as_handle(this: &mut AnyEventLoop<'static>) -> EventLoopHandle {
EventLoopHandle::from_any(this)
}
pub fn init() -> AnyEventLoop<'a> {
AnyEventLoop::Mini(Box::new(MiniEventLoop::init()))
}
#[inline]
pub fn js(js_event_loop: *mut ()) -> AnyEventLoop<'static> {
AnyEventLoop::Js {
owner: jsc_event_loop_handle(js_event_loop),
}
}
pub fn js_current() -> AnyEventLoop<'static> {
AnyEventLoop::Js {
owner: JsEventLoop::current(),
}
}
pub fn tick(
&mut self,
context: *mut core::ffi::c_void,
is_done: fn(*mut core::ffi::c_void) -> bool,
) {
match self {
AnyEventLoop::Js { owner } => {
while !is_done(context) {
owner.tick();
owner.auto_tick();
}
}
AnyEventLoop::Mini(mini) => mini.tick(context, is_done),
}
}
pub unsafe fn tick_raw(
this: *mut Self,
context: *mut core::ffi::c_void,
is_done: fn(*mut core::ffi::c_void) -> bool,
) {
while !is_done(context) {
match unsafe { &mut *this } {
AnyEventLoop::Js { owner } => {
owner.tick();
owner.auto_tick();
}
AnyEventLoop::Mini(mini) => {
mini.tick_once(context);
}
}
}
}
pub fn tick_once(&mut self, context: *mut core::ffi::c_void) {
match self {
AnyEventLoop::Js { owner } => {
let _ = context;
owner.tick();
owner.auto_tick_active();
}
AnyEventLoop::Mini(mini) => mini.tick_without_idle(context),
}
}
pub unsafe fn enqueue_task_concurrent<Context, ParentContext>(
&mut self,
ctx: *mut Context,
callback: fn(*mut Context, *mut ParentContext),
field_offset: usize,
) {
match self {
AnyEventLoop::Js { .. } => {
let _ = (ctx, callback, field_offset);
unreachable!("AnyEventLoop.enqueueTaskConcurrent");
}
AnyEventLoop::Mini(mini) => {
unsafe {
mini.enqueue_task_concurrent_with_extra_ctx::<Context, ParentContext>(
ctx,
callback,
field_offset,
);
}
}
}
}
}
impl AnyEventLoop<'static> {
#[inline]
pub fn r#loop(&mut self) -> *mut UwsLoop {
EventLoopHandle::from_any(self).r#loop()
}
#[inline]
pub fn loop_(&mut self) -> *mut UwsLoop {
self.r#loop()
}
#[inline]
pub fn native_loop(&mut self) -> *mut bun_io::Loop {
bun_io::uws_to_native(self.r#loop())
}
#[inline]
pub fn wakeup(&mut self) {
unsafe { (*self.r#loop()).wakeup() };
}
#[inline]
pub fn file_polls(&mut self) -> *mut bun_io::file_poll::Store {
EventLoopHandle::from_any(self).file_polls()
}
#[inline]
pub fn put_file_poll(&mut self, poll: &mut FilePoll) {
EventLoopHandle::from_any(self).put_file_poll(poll)
}
#[inline]
pub fn pipe_read_buffer(&mut self) -> *mut [u8] {
EventLoopHandle::from_any(self).pipe_read_buffer()
}
}
#[derive(Copy, Clone)]
pub enum EventLoopHandle {
Js {
owner: JsEventLoop,
},
Mini(BackRef<MiniEventLoop<'static>>),
}
#[inline]
fn mini_mut<'a>(mini: &'a mut BackRef<MiniEventLoop<'static>>) -> &'a mut MiniEventLoop<'static> {
unsafe { mini.get_mut() }
}
#[derive(Copy, Clone)]
pub union EventLoopTaskPtr {
pub js: *mut ConcurrentTask,
pub mini: *mut AnyTaskWithExtraContext,
}
pub enum EventLoopTask {
Js(ConcurrentTask),
Mini(AnyTaskWithExtraContext),
}
impl EventLoopTask {
pub fn init(kind: EventLoopKind) -> EventLoopTask {
match kind {
EventLoopKind::Js => EventLoopTask::Js(ConcurrentTask::default()),
EventLoopKind::Mini => EventLoopTask::Mini(AnyTaskWithExtraContext::default()),
}
}
pub fn from_event_loop(loop_: EventLoopHandle) -> EventLoopTask {
match loop_ {
EventLoopHandle::Js { .. } => EventLoopTask::Js(ConcurrentTask::default()),
EventLoopHandle::Mini(_) => EventLoopTask::Mini(AnyTaskWithExtraContext::default()),
}
}
}
#[must_use = "dropping immediately exits the event loop scope"]
pub struct EnteredEventLoop(EventLoopHandle);
impl Drop for EnteredEventLoop {
#[inline]
fn drop(&mut self) {
self.0.exit();
}
}
impl EventLoopHandle {
#[inline]
pub fn init(js_event_loop: *mut ()) -> EventLoopHandle {
EventLoopHandle::Js {
owner: jsc_event_loop_handle(js_event_loop),
}
}
#[inline]
pub fn init_mini(mini: *mut MiniEventLoop<'static>) -> EventLoopHandle {
EventLoopHandle::Mini(
NonNull::new(mini)
.expect("MiniEventLoop ptr is non-null")
.into(),
)
}
#[inline]
pub fn as_event_loop_ctx(self) -> bun_io::EventLoopCtx {
match self {
EventLoopHandle::Js { owner } => unsafe {
bun_io::EventLoopCtx::new(bun_io::EventLoopCtxKind::Js, owner.bun_vm())
},
EventLoopHandle::Mini(mut mini) => {
MiniEventLoop::as_event_loop_ctx(mini_mut(&mut mini))
}
}
}
#[inline]
pub fn into_tag_ptr(self) -> (core::ffi::c_char, *mut core::ffi::c_void) {
match self {
EventLoopHandle::Js { owner, .. } => (1, owner.owner.cast()),
EventLoopHandle::Mini(mini) => (2, mini.as_ptr().cast()),
}
}
#[inline]
pub unsafe fn from_tag_ptr(
tag: core::ffi::c_char,
ptr: *mut core::ffi::c_void,
) -> EventLoopHandle {
match tag {
1 => EventLoopHandle::Js {
owner: unsafe { JsEventLoop::new(JsEventLoopKind::Jsc, ptr.cast::<()>()) },
},
2 => EventLoopHandle::Mini(NonNull::new(ptr.cast()).expect("non-null mini ptr").into()),
_ => unreachable!("invalid parent event-loop tag {}", tag),
}
}
}
impl bun_uws::ParentEventLoopHandle for EventLoopHandle {
#[inline]
fn into_tag_ptr(self) -> (core::ffi::c_char, *mut core::ffi::c_void) {
EventLoopHandle::into_tag_ptr(self)
}
}
impl EventLoopHandle {
#[inline]
pub fn set_as_parent_of(self, uws_loop: &mut UwsLoop) {
let (tag, ptr) = self.into_tag_ptr();
uws_loop.internal_loop_data.set_parent_raw(tag, ptr);
}
pub fn from_any(any: &mut AnyEventLoop<'static>) -> EventLoopHandle {
match any {
AnyEventLoop::Js { owner } => EventLoopHandle::Js { owner: *owner },
AnyEventLoop::Mini(mini) => EventLoopHandle::Mini(BackRef::new_mut(&mut **mini)),
}
}
pub fn js_current() -> EventLoopHandle {
EventLoopHandle::Js {
owner: JsEventLoop::current(),
}
}
pub fn global_object(self) -> *mut () {
match self {
EventLoopHandle::Js { owner } => owner.global_object(),
EventLoopHandle::Mini(_) => core::ptr::null_mut(),
}
}
pub fn bun_vm(self) -> *mut () {
match self {
EventLoopHandle::Js { owner } => owner.bun_vm(),
EventLoopHandle::Mini(_) => core::ptr::null_mut(),
}
}
pub fn stdout(self) -> *mut () {
match self {
EventLoopHandle::Js { owner } => owner.stdout(),
EventLoopHandle::Mini(mut mini) => mini_mut(&mut mini).stdout(),
}
}
pub fn stderr(self) -> *mut () {
match self {
EventLoopHandle::Js { owner } => owner.stderr(),
EventLoopHandle::Mini(mut mini) => mini_mut(&mut mini).stderr(),
}
}
pub fn enter(self) {
if let EventLoopHandle::Js { owner } = self {
owner.enter();
}
}
pub fn exit(self) {
if let EventLoopHandle::Js { owner } = self {
owner.exit();
}
}
#[inline]
pub fn entered(self) -> EnteredEventLoop {
self.enter();
EnteredEventLoop(self)
}
pub fn file_polls(self) -> *mut bun_io::file_poll::Store {
match self {
EventLoopHandle::Js { owner } => owner.file_polls(),
EventLoopHandle::Mini(mut mini) => std::ptr::from_mut(mini_mut(&mut mini).file_polls()),
}
}
pub fn put_file_poll(&mut self, poll: &mut FilePoll) {
let was_ever_registered = poll
.flags
.contains(bun_io::file_poll::Flags::WasEverRegistered);
let poll_ptr = NonNull::from(poll);
match self {
EventLoopHandle::Js { owner } => {
owner.put_file_poll(poll_ptr.as_ptr(), was_ever_registered)
}
EventLoopHandle::Mini(mini) => {
let ctx = MiniEventLoop::as_event_loop_ctx(mini_mut(mini));
mini_mut(mini)
.file_polls()
.put(poll_ptr, ctx, was_ever_registered);
}
}
}
pub fn enqueue_task_concurrent(self, task: EventLoopTaskPtr) {
match self {
EventLoopHandle::Js { owner } => {
owner.enqueue_task_concurrent(unsafe { NonNull::new_unchecked(task.js) })
}
EventLoopHandle::Mini(mut mini) => {
let task = unsafe { NonNull::new_unchecked(task.mini) };
mini_mut(&mut mini).enqueue_task_concurrent(task);
}
}
}
pub fn r#loop(self) -> *mut UwsLoop {
match self {
EventLoopHandle::Js { owner } => owner.uws_loop(),
EventLoopHandle::Mini(mini) => mini.loop_ptr(),
}
}
#[inline]
pub fn platform_event_loop(self) -> *mut UwsLoop {
self.r#loop()
}
#[inline]
pub fn loop_(self) -> *mut UwsLoop {
self.r#loop()
}
#[inline]
pub fn native_loop(self) -> *mut bun_io::Loop {
bun_io::uws_to_native(self.r#loop())
}
#[cfg(windows)]
#[inline]
pub fn uv_loop(self) -> *mut bun_io::Loop {
self.native_loop()
}
pub fn pipe_read_buffer(self) -> *mut [u8] {
match self {
EventLoopHandle::Js { owner } => owner.pipe_read_buffer(),
EventLoopHandle::Mini(mut mini) => {
std::ptr::from_mut::<[u8]>(mini_mut(&mut mini).pipe_read_buffer())
}
}
}
pub fn ref_(self) {
unsafe { (*self.r#loop()).ref_() };
}
pub fn unref(self) {
unsafe { (*self.r#loop()).unref() };
}
pub fn env(self) -> *mut DotEnvLoader<'static> {
match self {
EventLoopHandle::Js { owner } => owner.env(),
EventLoopHandle::Mini(mini) => mini
.env_ptr()
.expect("MiniEventLoop.env unset")
.as_ptr()
.cast(),
}
}
pub fn top_level_dir(self) -> &'static [u8] {
match self {
EventLoopHandle::Js { owner } => unsafe { &*owner.top_level_dir() },
EventLoopHandle::Mini(mini) => unsafe { &(*mini.as_ptr()).top_level_dir },
}
}
pub fn create_null_delimited_env_map(
self,
) -> Result<bun_dotenv::NullDelimitedEnvMap, bun_core::AllocError> {
match self {
EventLoopHandle::Js { owner } => owner.create_null_delimited_env_map(),
EventLoopHandle::Mini(mini) => {
let env = mini.env_ptr().expect("MiniEventLoop.env unset");
unsafe { (*env.as_ptr()).map.create_null_delimited_env_map() }
}
}
}
}