use core::sync::atomic::{AtomicPtr, Ordering};
type ExecutorFn = unsafe fn(usize, unsafe fn(usize, *mut ()), *mut ());
#[must_use]
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct ParallelExecutor(ExecutorFn);
impl ParallelExecutor {
#[inline]
pub const unsafe fn new(executor: ExecutorFn) -> Self {
Self(executor)
}
#[inline]
fn as_ptr(self) -> *mut () {
self.0 as *mut ()
}
#[inline]
unsafe fn from_ptr(executor: *mut ()) -> Self {
Self(unsafe { core::mem::transmute::<*mut (), ExecutorFn>(executor) })
}
#[inline]
pub(super) unsafe fn execute(
self,
num_tasks: usize,
task_fn: unsafe fn(usize, *mut ()),
data: *mut (),
) {
unsafe { (self.0)(num_tasks, task_fn, data) }
}
}
const _: () =
assert!(core::mem::size_of::<ParallelExecutor>() == core::mem::size_of::<ExecutorFn>());
static PARALLEL_EXECUTOR: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
#[inline]
pub fn register_parallel_executor(executor: ParallelExecutor) {
PARALLEL_EXECUTOR.store(executor.as_ptr(), Ordering::Release);
}
#[inline]
pub fn clear_parallel_executor() {
PARALLEL_EXECUTOR.store(core::ptr::null_mut(), Ordering::Release);
}
#[inline]
pub(super) fn registered_parallel_executor() -> Option<ParallelExecutor> {
let executor_ptr = PARALLEL_EXECUTOR.load(Ordering::Acquire);
if executor_ptr.is_null() {
None
} else {
Some(unsafe { ParallelExecutor::from_ptr(executor_ptr) })
}
}