use std::{
ffi::{c_void, CString},
panic,
};
use static_assertions::assert_not_impl_any;
use crate::{rtl, sys, DataStore};
#[derive(Debug)]
pub struct AsyncTaskObject(sys::mint);
assert_not_impl_any!(AsyncTaskObject: Copy, Clone);
impl AsyncTaskObject {
pub fn spawn_with_thread<F>(f: F) -> Self
where
F: FnMut(AsyncTaskObject) + Send + panic::UnwindSafe + 'static,
{
spawn_async_task_with_thread(f)
}
pub fn id(&self) -> sys::mint {
let AsyncTaskObject(id) = *self;
id
}
pub fn is_alive(&self) -> bool {
let is_alive: sys::mbool = unsafe { rtl::asynchronousTaskAliveQ(self.id()) };
crate::bool_from_mbool(is_alive)
}
pub fn is_started(&self) -> bool {
let is_started: sys::mbool = unsafe { rtl::asynchronousTaskStartedQ(self.id()) };
crate::bool_from_mbool(is_started)
}
pub fn raise_async_event(&self, name: &str, data: DataStore) {
let AsyncTaskObject(id) = *self;
let name = CString::new(name)
.expect("unable to convert raised async event name to CString");
unsafe {
rtl::raiseAsyncEvent(id, name.into_raw(), data.into_raw());
}
}
}
fn spawn_async_task_with_thread<F>(task: F) -> AsyncTaskObject
where
F: FnMut(AsyncTaskObject) + Send + 'static + panic::UnwindSafe,
{
let boxed_closure = Box::into_raw(Box::new(task));
let task_id: sys::mint = unsafe {
rtl::createAsynchronousTaskWithThread(
Some(async_task_thread_trampoline::<F>),
boxed_closure as *mut c_void,
)
};
AsyncTaskObject(task_id)
}
unsafe extern "C" fn async_task_thread_trampoline<F>(
async_object_id: sys::mint,
boxed_closure: *mut c_void,
) where
F: FnMut(AsyncTaskObject) + Send + 'static + panic::UnwindSafe,
{
let boxed_closure: &mut F = &mut *(boxed_closure as *mut F);
match panic::catch_unwind(panic::AssertUnwindSafe(|| {
boxed_closure(AsyncTaskObject(async_object_id))
})) {
Ok(()) => (),
Err(_) => (),
}
}