mod header;
mod join;
#[cfg(all(test, loom))]
mod loom_tests;
pub(crate) mod names;
mod raw;
mod state;
#[cfg(all(test, not(loom)))]
mod tests;
use core::future::Future;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use core::task::{RawWaker, Waker};
use self::header::Header;
pub use self::join::{AbortHandle, FallibleTask, Join, JoinError, Task};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(pub(super) core::num::NonZeroU64);
impl core::fmt::Display for Id {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
std::thread_local! {
static CURRENT: core::cell::Cell<Option<Id>> = const { core::cell::Cell::new(None) };
}
pub(crate) fn current_id() -> Option<Id> {
CURRENT.try_with(core::cell::Cell::get).ok().flatten()
}
pub(super) fn enter(id: Id) -> Option<Id> {
CURRENT.try_with(|c| c.replace(Some(id))).ok().flatten()
}
pub(super) fn leave(previous: Option<Id>) {
let _ = CURRENT.try_with(|c| c.set(previous));
}
use self::raw::RawTask;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ScheduleInfo {
pub(crate) woken_while_running: bool,
}
impl ScheduleInfo {
pub(crate) const fn new(woken_while_running: bool) -> Self {
Self {
woken_while_running,
}
}
}
pub(crate) unsafe trait Schedule {
fn schedule(&self, runnable: Runnable, info: ScheduleInfo);
fn before_poll(&self, _id: Id) {}
fn after_poll(&self, _id: Id) {}
fn terminated(&self, _id: Id) {}
fn panicked(&self, _id: Id) {}
}
pub(crate) struct Runnable {
ptr: NonNull<()>,
}
unsafe impl Send for Runnable {}
unsafe impl Sync for Runnable {}
impl Runnable {
pub(crate) unsafe fn from_raw(ptr: NonNull<()>) -> Self {
Self { ptr }
}
pub(crate) fn into_raw(self) -> NonNull<()> {
ManuallyDrop::new(self).ptr
}
fn header(&self) -> &Header {
unsafe { self.ptr.cast::<Header>().as_ref() }
}
pub(crate) fn into_link(self) -> NonNull<crate::rt::inbox::Link> {
let header = self.into_raw().cast::<Header>();
unsafe { NonNull::new_unchecked(&raw mut (*header.as_ptr()).link) }
}
pub(crate) unsafe fn from_link(link: NonNull<crate::rt::inbox::Link>) -> Self {
let offset = core::mem::offset_of!(Header, link);
Self {
ptr: unsafe { NonNull::new_unchecked(link.as_ptr().cast::<u8>().sub(offset).cast()) },
}
}
pub(crate) fn run(self) -> bool {
let ptr = self.into_raw().as_ptr();
unsafe { ((*ptr.cast::<Header>()).vtable.run)(ptr) }
}
pub(crate) fn schedule(self) {
let ptr = self.into_raw().as_ptr();
unsafe { ((*ptr.cast::<Header>()).vtable.schedule)(ptr, ScheduleInfo::new(false)) }
}
pub(crate) fn waker(&self) -> Waker {
let ptr = self.ptr.as_ptr().cast_const();
let borrowed = ManuallyDrop::new(unsafe {
Waker::from_raw(RawWaker::new(ptr, &self.header().vtable.waker))
});
(*borrowed).clone()
}
}
impl Drop for Runnable {
fn drop(&mut self) {
use self::state::{AWAITER, CLOSED, COMPLETED, SCHEDULED};
let ptr = self.ptr.as_ptr().cast_const();
let header = self.header();
let mut state = header.state.load(crate::sync::internal::Ordering::Acquire);
loop {
if state & (COMPLETED | CLOSED) != 0 {
break;
}
match header.state.compare_exchange_weak(
state,
state | CLOSED,
crate::sync::internal::Ordering::AcqRel,
crate::sync::internal::Ordering::Acquire,
) {
Ok(_) => break,
Err(actual) => state = actual,
}
}
unsafe { (header.vtable.drop_future)(ptr) };
let state = header
.state
.fetch_and(!SCHEDULED, crate::sync::internal::Ordering::AcqRel);
if state & AWAITER != 0 {
header.notify(None);
}
unsafe { (header.vtable.drop_ref)(ptr) };
}
}
impl core::fmt::Debug for Runnable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Runnable").field("task", &self.ptr).finish()
}
}
pub(crate) fn spawn<F, S>(future: F, schedule: S) -> (Runnable, Task<F::Output>)
where
F: Future + Send + 'static,
F::Output: Send + 'static,
S: Schedule + Send + Sync + 'static,
{
unsafe { spawn_unchecked(future, schedule) }
}
pub(crate) unsafe fn spawn_unchecked<F, S>(future: F, schedule: S) -> (Runnable, Task<F::Output>)
where
F: Future,
S: Schedule,
{
let ptr = RawTask::<F, S>::allocate(future, schedule);
unsafe { (Runnable::from_raw(ptr), Task::from_raw(ptr)) }
}
#[inline]
pub(crate) fn abort_on_panic<R>(f: impl FnOnce() -> R) -> R {
struct Abort;
impl Drop for Abort {
fn drop(&mut self) {
std::process::abort();
}
}
let abort = Abort;
let out = f();
core::mem::forget(abort);
out
}