use super::par::FnContext;
use crate::{
ecs::{
sys::{
request::SystemBuffer,
system::{Invoke, SystemId},
},
worker::WorkerId,
},
global,
};
use my_utils::ds::{ManagedMutPtr, ReadyFuture, UnsafeFuture};
use std::{
any::Any,
cell::UnsafeCell,
mem,
ops::Deref,
ptr::NonNull,
sync::atomic::{self, AtomicI32, Ordering},
task::Poll,
};
#[derive(Debug)]
pub(crate) enum Task {
System(SysTask),
Parallel(ParTask),
Async(AsyncTask),
}
my_utils::impl_from_for_enum!("outer" = Task; "var" = System; "inner" = SysTask);
my_utils::impl_from_for_enum!("outer" = Task; "var" = Parallel; "inner" = ParTask);
my_utils::impl_from_for_enum!("outer" = Task; "var" = Async; "inner" = AsyncTask);
impl Task {
pub(super) fn id(&self) -> TaskId {
match self {
Self::System(task) => TaskId::System(task.sid),
Self::Parallel(task) => TaskId::Parallel(*task),
Self::Async(task) => TaskId::Async(**task),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(super) enum TaskId {
System(SystemId),
Parallel(ParTask),
Async(UnsafeFuture),
}
my_utils::impl_from_for_enum!("outer" = TaskId; "var" = System; "inner" = SystemId);
my_utils::impl_from_for_enum!("outer" = TaskId; "var" = Parallel; "inner" = ParTask);
my_utils::impl_from_for_enum!("outer" = TaskId; "var" = Async; "inner" = UnsafeFuture);
#[derive(Debug)]
pub(crate) struct SysTask {
invoker: ManagedMutPtr<dyn Invoke + Send>,
buf: ManagedMutPtr<SystemBuffer>,
sid: SystemId,
}
impl SysTask {
pub(super) const fn new(
invoker: ManagedMutPtr<dyn Invoke + Send>,
buf: ManagedMutPtr<SystemBuffer>,
sid: SystemId,
) -> Self {
Self { invoker, buf, sid }
}
pub(super) const fn sid(&self) -> SystemId {
self.sid
}
pub(super) fn execute(self, _wid: WorkerId) -> Result<(), Box<dyn Any + Send>> {
global::stat::increase_system_task_count();
#[cfg(target_arch = "wasm32")]
{
use super::comm::{TaskKind, WorkId, WORK_ID};
WORK_ID.with(|work_id| {
work_id.set(WorkId {
wid: _wid,
sid: self.sid,
kind: TaskKind::System,
})
});
}
#[cfg(not(target_arch = "wasm32"))]
{
let Self {
mut invoker,
mut buf,
..
} = self;
let executor = std::panic::AssertUnwindSafe(move || {
invoker.invoke(&mut buf);
});
std::panic::catch_unwind(executor)
}
#[cfg(target_arch = "wasm32")]
{
#[cfg(not(feature = "check"))]
{
let Self {
mut invoker,
mut buf,
..
} = self;
invoker.invoke(&mut buf);
}
#[cfg(feature = "check")]
{
let Self { invoker, buf, .. } = self;
let mut invoker_ptr = invoker.as_nonnull();
let mut buf_ptr = buf.as_nonnull();
drop(invoker);
drop(buf);
unsafe { invoker_ptr.as_mut().invoke(buf_ptr.as_mut()) };
}
Ok(())
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ParTask {
data: NonNull<u8>,
f: unsafe fn(NonNull<u8>, FnContext),
}
impl PartialEq for ParTask {
fn eq(&self, other: &Self) -> bool {
const _: () = {
assert!(mem::size_of::<ParTaskHolder::<fn(FnContext) -> (), ()>>() > 0);
};
self.data == other.data
}
}
impl Eq for ParTask {}
unsafe impl Send for ParTask {}
impl ParTask {
pub(super) unsafe fn new<F, R>(t: &ParTaskHolder<F, R>) -> Self
where
F: FnOnce(FnContext) -> R + Send,
{
let data = unsafe {
let ptr = t as *const _ as *const u8;
NonNull::new_unchecked(ptr.cast_mut())
};
let f = ParTaskHolder::<F, R>::execute;
Self { data, f }
}
pub(super) fn execute(self, _wid: WorkerId, f_cx: FnContext) {
#[cfg(target_arch = "wasm32")]
{
use super::comm::{TaskKind, WorkId, WORK_ID};
WORK_ID.with(|work_id| {
work_id.set(WorkId {
wid: _wid,
sid: SystemId::dummy(),
kind: TaskKind::Parallel,
})
});
}
unsafe { (self.f)(self.data, f_cx) };
}
}
#[derive(Debug)]
pub(super) struct ParTaskHolder<F, R> {
f: UnsafeCell<Option<F>>,
res: UnsafeCell<Option<R>>,
panic: UnsafeCell<Option<Box<dyn Any + Send>>>,
flag: ParTaskFlag,
}
impl<F, R> ParTaskHolder<F, R>
where
F: FnOnce(FnContext) -> R + Send,
{
pub(super) fn new(f: F) -> Self {
Self {
f: UnsafeCell::new(Some(f)),
res: UnsafeCell::new(None),
panic: UnsafeCell::new(None),
flag: ParTaskFlag::init(),
}
}
unsafe fn execute(data: NonNull<u8>, f_cx: FnContext) {
let this: &Self = unsafe { data.cast().as_ref() };
let f = unsafe { (*this.f.get()).take().unwrap_unchecked() };
#[cfg(not(target_arch = "wasm32"))]
{
let executor = std::panic::AssertUnwindSafe(move || f(f_cx));
match std::panic::catch_unwind(executor) {
Ok(res) => unsafe { *this.res.get() = Some(res) },
Err(payload) => unsafe { *this.panic.get() = Some(payload) },
}
this.flag.done();
}
#[cfg(target_arch = "wasm32")]
{
let res = f(f_cx);
unsafe { *this.res.get() = Some(res) };
this.flag.done();
}
}
pub(super) fn is_executed(&self) -> bool {
self.flag.is_done()
}
pub(super) unsafe fn return_or_panic_unchecked(self) -> Result<R, Box<dyn Any + Send>> {
if let Some(res) = self.res.into_inner() {
Ok(res)
} else {
let payload = unsafe { self.panic.into_inner().unwrap_unchecked() };
Err(payload)
}
}
}
#[derive(Debug)]
#[repr(transparent)]
struct ParTaskFlag(AtomicI32);
impl ParTaskFlag {
const INIT_INNER: i32 = 0;
const DONE_INNER: i32 = 1;
fn init() -> Self {
Self(AtomicI32::new(Self::INIT_INNER))
}
fn done(&self) {
self.0.store(Self::DONE_INNER, Ordering::Release);
}
fn is_done(&self) -> bool {
if self.0.load(Ordering::Relaxed) == Self::DONE_INNER {
atomic::fence(Ordering::Acquire);
true
} else {
false
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct AsyncTask(pub(crate) UnsafeFuture);
impl AsyncTask {
pub(super) fn execute<F>(self, _wid: WorkerId, on_ready: F)
where
F: FnOnce(ReadyFuture),
{
global::stat::increase_future_task_count();
#[cfg(target_arch = "wasm32")]
{
use super::comm::{TaskKind, WorkId, WORK_ID};
WORK_ID.with(|work_id| {
work_id.set(WorkId {
wid: _wid,
sid: SystemId::dummy(),
kind: TaskKind::Async,
});
});
}
match unsafe { self.poll() } {
Poll::Ready(()) => {
let ready = unsafe { ReadyFuture::new(*self) };
on_ready(ready)
}
Poll::Pending => {}
}
}
}
impl Deref for AsyncTask {
type Target = UnsafeFuture;
fn deref(&self) -> &Self::Target {
&self.0
}
}