use std::sync::OnceLock;
use crate::ThreadPool;
pub use crate::thread_pool::Batch;
pub use crate::thread_pool::Task;
pub struct WorkPool;
pub unsafe trait IntrusiveWorkTask: bun_core::IntrusiveField<Task> {
#[inline]
fn task_mut(&mut self) -> &mut Task {
self.field_mut()
}
#[inline(always)]
unsafe fn from_task_ptr(task: *mut Task) -> *mut Self {
unsafe { Self::from_field_ptr(task) }
}
}
pub unsafe trait OwnedTask: IntrusiveWorkTask + Send + 'static {
fn run(self: Box<Self>);
#[doc(hidden)]
unsafe fn __callback(task: *mut Task) {
let this = unsafe { Box::from_raw(Self::from_task_ptr(task)) };
this.run();
}
}
#[macro_export]
macro_rules! intrusive_work_task {
([$($gen:tt)*] $ty:ty, $field:ident) => {
::bun_core::intrusive_field!([$($gen)*] $ty, $field: $crate::work_pool::Task);
unsafe impl<$($gen)*> $crate::work_pool::IntrusiveWorkTask for $ty {}
};
($ty:ty, $field:ident) => {
::bun_core::intrusive_field!($ty, $field: $crate::work_pool::Task);
unsafe impl $crate::work_pool::IntrusiveWorkTask for $ty {}
};
}
#[macro_export]
macro_rules! owned_task {
([$($gen:tt)*] $ty:ty, $field:ident) => {
$crate::intrusive_work_task!([$($gen)*] $ty, $field);
unsafe impl<$($gen)*> ::core::marker::Send for $ty {}
unsafe impl<$($gen)*> $crate::work_pool::OwnedTask for $ty {
#[inline]
fn run(self: ::std::boxed::Box<Self>) { <$ty>::run_owned(self) }
}
};
($ty:ty, $field:ident) => {
$crate::intrusive_work_task!($ty, $field);
unsafe impl ::core::marker::Send for $ty {}
unsafe impl $crate::work_pool::OwnedTask for $ty {
#[inline]
fn run(self: ::std::boxed::Box<Self>) { <$ty>::run_owned(self) }
}
};
}
static POOL: OnceLock<ThreadPool> = OnceLock::new();
#[cold]
fn create() -> ThreadPool {
ThreadPool::init(crate::thread_pool::Config {
max_threads: u32::from(bun_core::get_thread_count()),
stack_size: crate::thread_pool::DEFAULT_THREAD_STACK_SIZE,
})
}
impl WorkPool {
#[inline]
pub fn get() -> &'static ThreadPool {
POOL.get_or_init(create)
}
pub fn schedule_batch(batch: Batch) {
Self::get().schedule(batch);
}
pub fn schedule(task: *mut Task) {
Self::get().schedule(Batch::from(task));
}
pub fn schedule_owned<T: OwnedTask>(mut task: Box<T>) {
task.task_mut().callback = T::__callback;
let raw = Box::into_raw(task);
Self::schedule(unsafe { T::field_of(raw) });
}
#[inline]
pub fn schedule_new<T: OwnedTask>(task: T) {
Self::schedule_owned(Box::new(task));
}
pub fn go<C: Send + 'static>(context: C, function: fn(C)) -> Result<(), bun_alloc::AllocError> {
#[repr(C)]
struct TaskType<C> {
task: Task,
context: C,
function: fn(C),
}
unsafe fn callback<C>(task: *mut Task) {
unsafe {
let this_task = bun_core::from_field_ptr!(TaskType<C>, task, task);
let this_task = Box::from_raw(this_task);
(this_task.function)(this_task.context);
}
}
let task_ = Box::into_raw(Box::new(TaskType::<C> {
task: Task {
node: crate::thread_pool::Node::default(),
callback: callback::<C>,
},
context,
function,
}));
Self::schedule(unsafe { &raw mut (*task_).task });
Ok(())
}
}