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, REFERENCE, 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,
),
};
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 {
let cell = Self::cell(ptr);
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,
);
}
}
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 mut state = header.state.load(Ordering::Acquire);
loop {
if state & CLOSED != 0 {
Self::drop_future(ptr);
let state = header.state.fetch_and(!SCHEDULED, Ordering::AcqRel);
Self::finish(ptr, state);
return false;
}
match header.state.compare_exchange_weak(
state,
(state & !SCHEDULED) | RUNNING,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => break,
Err(actual) => state = actual,
}
}
let previous = super::enter(header.id);
let budget = crate::coop::enter();
let hooked =
crate::rt::tuning::ANY_POLL_HOOKS.load(core::sync::atomic::Ordering::Relaxed);
if hooked {
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))
}));
if hooked {
cell.schedule.after_poll(header.id);
}
crate::coop::leave(budget);
super::leave(previous);
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(ptr);
cell.core
.with_mut(|core| (*core).output = ManuallyDrop::new(output));
loop {
let done = (state & !RUNNING & !SCHEDULED) | COMPLETED;
let new = if state & TASK == 0 {
done | CLOSED
} else {
done
};
match header.state.compare_exchange_weak(
state,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
if state & TASK == 0 || state & CLOSED != 0 {
abort_on_panic(|| {
cell.core.with_mut(|core| {
ManuallyDrop::drop(&mut (*core).output)
})
});
}
if panicked {
cell.schedule.panicked(header.id);
}
Self::finish(ptr, state);
return false;
}
Err(actual) => state = actual,
}
}
}
None => {
let mut future_dropped = false;
loop {
if state & CLOSED != 0 && !future_dropped {
Self::drop_future(ptr);
future_dropped = true;
}
let new = if state & CLOSED != 0 {
state & !RUNNING & !SCHEDULED
} else {
state & !RUNNING
};
match header.state.compare_exchange_weak(
state,
new,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) if state & CLOSED != 0 => {
Self::finish(ptr, state);
return false;
}
Ok(_) if state & SCHEDULED != 0 => {
Self::schedule(ptr, ScheduleInfo::new(true));
return true;
}
Ok(_) => {
Self::drop_ref(ptr);
return false;
}
Err(actual) => state = actual,
}
}
}
}
}
}
}