extern crate alloc;
use std::alloc::{Layout, dealloc};
use std::future::Future;
use std::mem::ManuallyDrop;
use std::panic::{RefUnwindSafe, UnwindSafe};
use crate::loom_exports::sync::atomic::{self, Ordering};
use super::Task;
use super::runnable::Runnable;
use super::util::{RunOnDrop, runnable_exists};
use super::{CLOSED, POLLING, REF_INC, REF_MASK};
#[derive(Debug)]
struct VTable {
cancel: unsafe fn(*const ()),
drop: unsafe fn(*const ()),
}
unsafe fn cancel<F, S, T>(ptr: *const ())
where
F: Future + Send + 'static,
F::Output: Send + 'static,
S: Fn(Runnable, T) + Send + Sync + 'static,
T: Clone + Send + Sync + 'static,
{
let this = unsafe { &*(ptr as *const Task<F, S, T>) };
let state = this
.state
.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |s| {
if s & POLLING == 0 {
Some(s - REF_INC)
} else if runnable_exists(s) {
Some((s | CLOSED) - REF_INC)
} else {
Some((s | CLOSED) & !POLLING)
}
})
.unwrap();
if runnable_exists(state) {
return;
}
if state & POLLING == 0 {
if state & REF_MASK == REF_INC {
atomic::fence(Ordering::Acquire);
let _drop_guard = RunOnDrop::new(|| {
unsafe { dealloc(ptr as *mut u8, Layout::new::<Task<F, S, T>>()) };
});
if state & CLOSED == 0 {
this.core
.with_mut(|c| unsafe { ManuallyDrop::drop(&mut (*c).output) });
}
}
return;
}
let _drop_guard = RunOnDrop::new(|| {
let state = this.state.fetch_sub(REF_INC, Ordering::Release);
if state & REF_MASK == REF_INC {
atomic::fence(Ordering::Acquire);
unsafe { dealloc(ptr as *mut u8, Layout::new::<Task<F, S, T>>()) };
}
});
this.core
.with_mut(|c| unsafe { ManuallyDrop::drop(&mut (*c).future) });
}
unsafe fn drop<F, S, T>(ptr: *const ())
where
F: Future + Send + 'static,
F::Output: Send + 'static,
S: Fn(Runnable, T) + Send + Sync + 'static,
T: Clone + Send + Sync + 'static,
{
let this = unsafe { &*(ptr as *const Task<F, S, T>) };
let state = this.state.fetch_sub(REF_INC, Ordering::Release);
if state & REF_MASK == REF_INC && !runnable_exists(state) {
atomic::fence(Ordering::Acquire);
let _drop_guard = RunOnDrop::new(|| {
unsafe { dealloc(ptr as *mut u8, Layout::new::<Task<F, S, T>>()) };
});
unsafe {
if state & POLLING == POLLING {
this.core.with_mut(|c| ManuallyDrop::drop(&mut (*c).future));
} else if state & CLOSED == 0 {
this.core.with_mut(|c| ManuallyDrop::drop(&mut (*c).output));
}
}
}
}
#[derive(Debug)]
pub(crate) struct CancelToken {
task: *const (),
vtable: &'static VTable,
}
impl CancelToken {
pub(super) unsafe fn new_unchecked<F, S, T>(task: *const Task<F, S, T>) -> Self
where
F: Future + Send + 'static,
F::Output: Send + 'static,
S: Fn(Runnable, T) + Send + Sync + 'static,
T: Clone + Send + Sync + 'static,
{
Self {
task: task as *const (),
vtable: &VTable {
cancel: cancel::<F, S, T>,
drop: drop::<F, S, T>,
},
}
}
pub(crate) fn cancel(self) {
let this = ManuallyDrop::new(self);
unsafe { (this.vtable.cancel)(this.task) }
}
}
impl Drop for CancelToken {
fn drop(&mut self) {
unsafe { (self.vtable.drop)(self.task) }
}
}
unsafe impl Send for CancelToken {}
impl UnwindSafe for CancelToken {}
impl RefUnwindSafe for CancelToken {}