use std::{
cell::UnsafeCell,
ffi::c_void,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
use vtable_rs::VPtr;
pub trait SharedTaskImp<TIndex, TTaskData: Send + 'static> {
fn register_task_internal(&self, index: TIndex, task: &RecurringTask<TTaskData>);
}
pub trait SharedTaskImpExt<TIndex, TTaskData: Send + 'static> {
fn run_recurring<T: Into<RecurringTask<TTaskData>>>(
&self,
execute: T,
group: TIndex,
) -> RecurringTaskHandle<TTaskData>;
}
impl<TIndex, TTaskData: Send + 'static, S: SharedTaskImp<TIndex, TTaskData>>
SharedTaskImpExt<TIndex, TTaskData> for S
{
fn run_recurring<T: Into<RecurringTask<TTaskData>>>(
&self,
task: T,
group: TIndex,
) -> RecurringTaskHandle<TTaskData> {
#[allow(clippy::arc_with_non_send_sync)]
let task: Arc<RecurringTask<TTaskData>> = Arc::new(task.into());
unsafe {
*task.self_ref.get() = Some(task.clone());
}
self.register_task_internal(group, task.as_ref());
RecurringTaskHandle { _task: task }
}
}
pub struct RecurringTaskHandle<TTaskData: Send + 'static> {
_task: Arc<RecurringTask<TTaskData>>,
}
impl<TTaskData: Send + 'static> Drop for RecurringTaskHandle<TTaskData> {
fn drop(&mut self) {
self._task.cancel();
}
}
#[repr(C)]
pub struct RecurringTask<TTaskData: Send + 'static> {
vftable: VPtr<dyn SharedTaskBaseVmt, Self>,
unk8: usize,
closure: Box<dyn FnMut(&TTaskData)>,
unregister_requested: AtomicBool,
self_ref: UnsafeCell<Option<Arc<Self>>>,
}
impl<TTaskData: Send + 'static> RecurringTask<TTaskData> {
pub fn new<F: FnMut(&TTaskData) + 'static + Send>(closure: F) -> Self {
Self {
vftable: Default::default(),
unk8: 0,
closure: Box::new(closure),
unregister_requested: AtomicBool::new(false),
self_ref: UnsafeCell::new(None),
}
}
pub fn cancel(&self) {
self.unregister_requested.store(true, Ordering::Relaxed);
}
}
impl<TTaskData: Send + 'static> SharedTaskBaseVmt for RecurringTask<TTaskData> {
extern "C" fn get_runtime_class(&self) -> usize {
unimplemented!();
}
extern "C" fn destructor(&mut self) {
unimplemented!();
}
extern "C" fn execute(&mut self, data: *const c_void) {
(self.closure)(unsafe { &*(data as *const TTaskData) });
}
}
impl<TTaskData: Send + 'static, F: FnMut(&TTaskData) + 'static + Send> From<F>
for RecurringTask<TTaskData>
{
fn from(value: F) -> Self {
Self::new(value)
}
}
#[vtable_rs::vtable]
trait SharedTaskBaseVmt {
fn get_runtime_class(&self) -> usize;
fn destructor(&mut self);
fn execute(&mut self, data: *const c_void);
}