use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use super::LocalBoxFuture;
use super::handles::ThreadHandle;
use super::state::{describe_panic, try_with_installed_thread, with_installed_thread};
use crate::task::JoinError;
use crate::trace_targets;
pub(crate) struct FutureTask {
pub(crate) future: RefCell<Option<LocalBoxFuture>>,
pub(crate) queued: Cell<bool>,
pub(crate) shared: Rc<TaskShared>,
pub(crate) id: u64,
waker: Waker,
}
impl FutureTask {
pub(crate) fn new(
future: LocalBoxFuture,
shared: Rc<TaskShared>,
id: u64,
owner: ThreadHandle,
) -> Rc<Self> {
let waker_data = Arc::new(TaskWaker {
owner,
id,
scheduled: AtomicBool::new(false),
});
Rc::new(Self {
future: RefCell::new(Some(future)),
queued: Cell::new(false),
shared,
id,
waker: TaskWaker::into_waker(waker_data),
})
}
pub(crate) fn schedule(self: &Rc<Self>) {
if self.queued.replace(true) {
return;
}
let task = Rc::clone(self);
with_installed_thread(|state| {
state
.local_microtasks
.borrow_mut()
.push_back(Box::new(move || task.poll()));
});
}
fn poll(self: Rc<Self>) {
self.queued.set(false);
let Some(mut future) = self.future.borrow_mut().take() else {
return;
};
let mut context = Context::from_waker(&self.waker);
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
future.as_mut().poll(&mut context)
}));
match outcome {
Ok(Poll::Ready(())) => {
deregister_task(self.id);
}
Ok(Poll::Pending) => {
if self.shared.is_aborted() {
drop(future);
} else {
*self.future.borrow_mut() = Some(future);
}
}
Err(payload) => {
tracing::error!(
target: trace_targets::ASYNC,
event = "task_panicked",
task_id = self.id,
panic = describe_panic(&*payload),
"spawned task panicked; isolating and reporting JoinError::Panicked to the joiner",
);
drop(future);
deregister_task(self.id);
self.shared.mark_panicked();
}
}
}
}
struct TaskWaker {
owner: ThreadHandle,
id: u64,
scheduled: AtomicBool,
}
impl TaskWaker {
fn into_waker(data: Arc<Self>) -> Waker {
unsafe {
Waker::from_raw(RawWaker::new(
Arc::into_raw(data).cast::<()>(),
&TASK_WAKER_VTABLE,
))
}
}
fn wake(self: &Arc<Self>) {
if self.scheduled.swap(true, Ordering::AcqRel) {
return;
}
if self.owner.is_current() {
self.scheduled.store(false, Ordering::Release);
schedule_task_by_id(self.id);
return;
}
let this = Arc::clone(self);
let id = self.id;
if self
.owner
.queue_internal_wake(move || {
this.scheduled.store(false, Ordering::Release);
schedule_task_by_id(id);
})
.is_err()
{
self.scheduled.store(false, Ordering::Release);
}
}
}
fn schedule_task_by_id(id: u64) {
let task = with_installed_thread(|state| state.tasks.borrow().get(&id).map(Rc::clone));
if let Some(task) = task {
task.schedule();
}
}
fn deregister_task(id: u64) {
try_with_installed_thread(|state| {
if let Some(state) = state {
state.tasks.borrow_mut().remove(&id);
}
});
}
static TASK_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
task_waker_clone,
task_waker_wake,
task_waker_wake_by_ref,
task_waker_drop,
);
unsafe fn task_waker_clone(data: *const ()) -> RawWaker {
let arc = unsafe { Arc::<TaskWaker>::from_raw(data.cast::<TaskWaker>()) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
RawWaker::new(Arc::into_raw(cloned).cast::<()>(), &TASK_WAKER_VTABLE)
}
unsafe fn task_waker_wake(data: *const ()) {
let arc = unsafe { Arc::<TaskWaker>::from_raw(data.cast::<TaskWaker>()) };
arc.wake();
}
unsafe fn task_waker_wake_by_ref(data: *const ()) {
let arc = unsafe { Arc::<TaskWaker>::from_raw(data.cast::<TaskWaker>()) };
arc.wake();
let _ = Arc::into_raw(arc);
}
unsafe fn task_waker_drop(data: *const ()) {
drop(unsafe { Arc::<TaskWaker>::from_raw(data.cast::<TaskWaker>()) });
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum TaskState {
Running,
Finished,
Aborted,
Panicked,
}
pub(crate) struct TaskShared {
task: RefCell<Weak<FutureTask>>,
join_waker: RefCell<Option<Waker>>,
state: Cell<TaskState>,
id: Cell<u64>,
}
impl TaskShared {
pub(crate) fn new() -> Self {
Self {
task: RefCell::new(Weak::new()),
join_waker: RefCell::new(None),
state: Cell::new(TaskState::Running),
id: Cell::new(0),
}
}
pub(crate) fn set_task(&self, task: &Rc<FutureTask>) {
*self.task.borrow_mut() = Rc::downgrade(task);
self.id.set(task.id);
}
pub(crate) fn is_finished(&self) -> bool {
!matches!(self.state.get(), TaskState::Running)
}
fn is_aborted(&self) -> bool {
matches!(self.state.get(), TaskState::Aborted)
}
pub(crate) fn abort(&self) {
if !matches!(self.state.get(), TaskState::Running) {
return;
}
self.state.set(TaskState::Aborted);
if let Some(task) = self.task.borrow().upgrade() {
let _ = task.future.borrow_mut().take();
}
deregister_task(self.id.get());
if let Some(waker) = self.join_waker.borrow_mut().take() {
waker.wake();
}
}
fn mark_panicked(&self) {
if !matches!(self.state.get(), TaskState::Running) {
return;
}
self.state.set(TaskState::Panicked);
if let Some(waker) = self.join_waker.borrow_mut().take() {
waker.wake();
}
}
}
pub(crate) struct JoinState<T> {
pub(crate) shared: Rc<TaskShared>,
result: RefCell<Option<T>>,
}
impl<T> JoinState<T> {
pub(crate) fn new(shared: Rc<TaskShared>) -> Self {
Self {
shared,
result: RefCell::new(None),
}
}
pub(crate) fn complete(&self, value: T) {
if !matches!(self.shared.state.get(), TaskState::Running) {
return;
}
*self.result.borrow_mut() = Some(value);
self.shared.state.set(TaskState::Finished);
if let Some(waker) = self.shared.join_waker.borrow_mut().take() {
waker.wake();
}
}
pub(crate) fn poll(&self, cx: &mut Context<'_>) -> Poll<Result<T, JoinError>> {
match self.shared.state.get() {
TaskState::Finished => Poll::Ready(Ok(self
.result
.borrow_mut()
.take()
.expect("join handle polled after completion"))),
TaskState::Aborted => Poll::Ready(Err(JoinError::Aborted)),
TaskState::Panicked => Poll::Ready(Err(JoinError::Panicked)),
TaskState::Running => {
*self.shared.join_waker.borrow_mut() = Some(cx.waker().clone());
Poll::Pending
}
}
}
}