use core::future::Future;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::ptr::NonNull;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use std::any::Any;
use std::panic::{AssertUnwindSafe, catch_unwind};
use super::header::Header;
use super::state::{
AWAITER, CLOSED, COMPLETED, FLAGS, NOTIFYING, REFERENCE, REGISTERING, RUNNING, SCHEDULED, TASK,
};
use super::{Runnable, Schedule, ScheduleInfo, abort_on_panic};
use crate::sync::internal::{Ordering, UnsafeCell};
pub(crate) type Panic = Box<dyn Any + Send + 'static>;
pub(super) struct TaskVTable {
pub(super) schedule: unsafe fn(*const (), ScheduleInfo),
pub(super) drop_future: unsafe fn(*const ()),
pub(super) read_output: unsafe fn(*const (), *mut ()),
pub(super) drop_ref: unsafe fn(*const ()),
pub(super) destroy: unsafe fn(*const ()),
pub(super) run: unsafe fn(*const ()) -> bool,
pub(super) waker: RawWakerVTable,
}
#[repr(C)]
struct Cell<F: Future, S> {
header: Header,
schedule: S,
core: UnsafeCell<Core<F>>,
}
union Core<F: Future> {
future: ManuallyDrop<F>,
output: ManuallyDrop<Result<F::Output, Panic>>,
}
pub(super) struct RawTask<F, S>(PhantomData<(F, S)>);
impl<F, S> RawTask<F, S>
where
F: Future,
S: Schedule,
{
const VTABLE: TaskVTable = TaskVTable {
schedule: Self::schedule,
drop_future: Self::drop_future,
read_output: Self::read_output,
drop_ref: Self::drop_ref,
destroy: Self::destroy,
run: Self::run,
waker: RawWakerVTable::new(
Self::clone_waker,
Self::wake,
Self::wake_by_ref,
Self::drop_waker,
),
};
#[inline(always)]
pub(super) fn allocate(future: F, schedule: S) -> NonNull<()> {
let (ptr, owner) = crate::mem::alloc(core::alloc::Layout::new::<Cell<F, S>>());
let ptr = ptr.cast::<Cell<F, S>>();
unsafe {
ptr.as_ptr().write(Cell {
header: Header::new(&Self::VTABLE, owner),
schedule,
core: UnsafeCell::new(Core {
future: ManuallyDrop::new(future),
}),
});
}
ptr.cast()
}
unsafe fn cell<'a>(ptr: *const ()) -> &'a Cell<F, S> {
unsafe { &*ptr.cast::<Cell<F, S>>() }
}
unsafe fn clone_waker(ptr: *const ()) -> RawWaker {
let header = unsafe { &Self::cell(ptr).header };
let state = header.state.fetch_add(REFERENCE, Ordering::Relaxed);
if state > isize::MAX as usize {
std::process::abort();
}
RawWaker::new(ptr, &header.vtable.waker)
}
unsafe fn wake(ptr: *const ()) {
unsafe {
let header = &Self::cell(ptr).header;
let mut state = header.state.load(Ordering::Acquire);
loop {
if state & (COMPLETED | CLOSED) != 0 {
Self::drop_waker(ptr);
return;
}
let new = state | SCHEDULED;
match header.state.compare_exchange_weak(
state,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) if state & (SCHEDULED | RUNNING) == 0 => {
Self::schedule(ptr, ScheduleInfo::new(false));
return;
}
Ok(_) => {
Self::drop_waker(ptr);
return;
}
Err(actual) => state = actual,
}
}
}
}
unsafe fn wake_by_ref(ptr: *const ()) {
unsafe {
let header = &Self::cell(ptr).header;
let mut state = header.state.load(Ordering::Acquire);
loop {
if state & (COMPLETED | CLOSED) != 0 {
return;
}
let idle = state & (SCHEDULED | RUNNING) == 0;
let new = if idle {
(state | SCHEDULED) + REFERENCE
} else {
state | SCHEDULED
};
match header.state.compare_exchange_weak(
state,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
if idle {
if state > isize::MAX as usize {
std::process::abort();
}
Self::schedule(ptr, ScheduleInfo::new(false));
}
return;
}
Err(actual) => state = actual,
}
}
}
}
unsafe fn drop_waker(ptr: *const ()) {
unsafe {
let header = &Self::cell(ptr).header;
let new = header.state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE;
if new & !FLAGS == 0 && new & TASK == 0 {
if new & (COMPLETED | CLOSED) == 0 {
header
.state
.store(SCHEDULED | CLOSED | REFERENCE, Ordering::Release);
Self::schedule(ptr, ScheduleInfo::new(false));
} else {
Self::destroy(ptr);
}
}
}
}
unsafe fn drop_ref(ptr: *const ()) {
unsafe {
let header = &Self::cell(ptr).header;
let new = header.state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE;
if new & !FLAGS == 0 && new & TASK == 0 {
if new & (COMPLETED | CLOSED) == 0 {
Self::drop_future(ptr);
}
Self::destroy(ptr);
}
}
}
unsafe fn schedule(ptr: *const (), info: ScheduleInfo) {
unsafe {
let runnable = Runnable::from_raw(NonNull::new_unchecked(ptr.cast_mut()));
Self::cell(ptr).schedule.schedule(runnable, info);
}
}
unsafe fn drop_future(ptr: *const ()) {
unsafe { Self::drop_future_here(ptr) }
}
#[inline(always)]
unsafe fn drop_future_here(ptr: *const ()) {
unsafe {
let cell = Self::cell(ptr);
cell.schedule.drop_future(|| {
abort_on_panic(|| {
cell.core
.with_mut(|core| ManuallyDrop::drop(&mut (*core).future))
})
});
cell.schedule.terminated(cell.header.id);
}
}
unsafe fn read_output(ptr: *const (), out: *mut ()) {
unsafe {
let cell = Self::cell(ptr);
cell.core.with_mut(|core| {
out.cast::<Result<F::Output, Panic>>()
.write(ManuallyDrop::take(&mut (*core).output));
});
}
}
unsafe fn destroy(ptr: *const ()) {
unsafe {
let header = &Self::cell(ptr).header;
super::names::forget(header.id);
let owner = header.owner;
let cell = ptr.cast_mut().cast::<Cell<F, S>>();
abort_on_panic(|| core::ptr::drop_in_place(cell));
crate::mem::free(
NonNull::new_unchecked(cell.cast()),
core::alloc::Layout::new::<Cell<F, S>>(),
owner,
);
}
}
#[inline(always)]
unsafe fn finish(ptr: *const (), state: usize) {
unsafe {
let header = &Self::cell(ptr).header;
let awaiter = if state & AWAITER != 0 {
header.take(None)
} else {
None
};
Self::drop_ref(ptr);
if let Some(waker) = awaiter {
abort_on_panic(|| waker.wake());
}
}
}
unsafe fn run(ptr: *const ()) -> bool {
unsafe {
let cell = Self::cell(ptr);
let header = &cell.header;
let waker =
ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &header.vtable.waker)));
let cx = &mut Context::from_waker(&waker);
let state = header
.state
.fetch_add(RUNNING - SCHEDULED, Ordering::AcqRel);
debug_assert_eq!(state & (SCHEDULED | RUNNING), SCHEDULED);
if state & CLOSED != 0 {
Self::drop_future(ptr);
let state = header.state.fetch_and(!RUNNING, Ordering::AcqRel);
Self::finish(ptr, state);
return false;
}
let entered = crate::coop::enter_task(header.id);
cell.schedule.before_poll(header.id);
let poll = catch_unwind(AssertUnwindSafe(|| {
cell.core
.with_mut(|core| F::poll(Pin::new_unchecked(&mut *(*core).future), cx))
}));
cell.schedule.after_poll(header.id);
crate::coop::leave_task(entered);
let mut state = header.state.load(Ordering::Acquire);
let output = match poll {
Ok(Poll::Pending) => None,
Ok(Poll::Ready(value)) => Some(Ok(value)),
Err(payload) => Some(Err(payload)),
};
match output {
Some(output) => {
let panicked = output.is_err();
Self::drop_future_here(ptr);
cell.core
.with_mut(|core| (*core).output = ManuallyDrop::new(output));
if panicked {
cell.schedule.panicked(header.id);
}
let mut awaiter = None;
let mut claimed = false;
let mut unclaimed = None;
let last = loop {
if !claimed
&& state & AWAITER != 0
&& state & (NOTIFYING | REGISTERING) == 0
{
let (waker, ours, now) = header.claim();
if ours {
awaiter = waker;
claimed = true;
}
state = now;
}
if (state & TASK == 0 || state & CLOSED != 0) && unclaimed.is_none() {
unclaimed = Some(
cell.core
.with_mut(|core| ManuallyDrop::take(&mut (*core).output)),
);
}
let mut new = (state & !RUNNING & !SCHEDULED) | COMPLETED;
if state & TASK == 0 {
new |= CLOSED;
}
if claimed {
new &= !(NOTIFYING | AWAITER);
}
new -= REFERENCE;
match header.state.compare_exchange_weak(
state,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => break new & !FLAGS == 0 && new & TASK == 0,
Err(actual) => state = actual,
}
};
if last {
Self::destroy(ptr);
}
if let Some(output) = unclaimed {
abort_on_panic(|| drop(output));
}
if let Some(waker) = awaiter {
abort_on_panic(|| waker.wake());
}
false
}
None => loop {
if state & CLOSED != 0 {
return Self::end_closed(ptr, state);
}
let new = if state & SCHEDULED != 0 {
state & !RUNNING
} else {
(state & !RUNNING) - REFERENCE
};
match header.state.compare_exchange_weak(
state,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) if state & SCHEDULED != 0 => {
Self::schedule(ptr, ScheduleInfo::new(true));
return true;
}
Ok(_) => {
if new & !FLAGS == 0 && new & TASK == 0 {
Self::drop_future(ptr);
Self::destroy(ptr);
}
return false;
}
Err(actual) => state = actual,
}
},
}
}
}
#[cold]
#[inline(never)]
unsafe fn end_closed(ptr: *const (), mut state: usize) -> bool {
unsafe {
Self::drop_future(ptr);
let header = &Self::cell(ptr).header;
loop {
match header.state.compare_exchange_weak(
state,
state & !RUNNING & !SCHEDULED,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
Self::finish(ptr, state);
return false;
}
Err(actual) => state = actual,
}
}
}
}
}