1use alloc::boxed::Box;
2use alloc::rc::Rc;
3use core::cell::{Cell, RefCell};
4use core::future::Future;
5use core::mem::ManuallyDrop;
6use core::pin::Pin;
7use core::task::{Context, RawWaker, RawWakerVTable, Waker};
89struct Inner {
10 future: Pin<Box<dyn Future<Output = ()> + 'static>>,
11 waker: Waker,
12}
1314pub(crate) struct Task {
15// The actual Future that we're executing as part of this task.
16 //
17 // This is an Option so that the Future can be immediately dropped when it's
18 // finished
19inner: RefCell<Option<Inner>>,
2021// This is used to ensure that the Task will only be queued once
22is_queued: Cell<bool>,
23}
2425impl Task {
26pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) {
27let this = Rc::new(Self {
28 inner: RefCell::new(None),
29 is_queued: Cell::new(true),
30 });
3132let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) };
3334*this.inner.borrow_mut() = Some(Inner { future, waker });
3536crate::queue::Queue::with(|queue| queue.schedule_task(this));
37 }
3839fn force_wake(this: Rc<Self>) {
40crate::queue::Queue::with(|queue| {
41 queue.push_task(this);
42 });
43 }
4445fn wake(this: Rc<Self>) {
46// If we've already been placed on the run queue then there's no need to
47 // requeue ourselves since we're going to run at some point in the
48 // future anyway.
49if this.is_queued.replace(true) {
50return;
51 }
5253Self::force_wake(this);
54 }
5556fn wake_by_ref(this: &Rc<Self>) {
57// If we've already been placed on the run queue then there's no need to
58 // requeue ourselves since we're going to run at some point in the
59 // future anyway.
60if this.is_queued.replace(true) {
61return;
62 }
6364Self::force_wake(Rc::clone(this));
65 }
6667/// Creates a standard library `RawWaker` from an `Rc` of ourselves.
68 ///
69 /// Note that in general this is wildly unsafe because everything with
70 /// Futures requires `Sync` + `Send` with regard to Wakers. For wasm,
71 /// however, everything is guaranteed to be singlethreaded (since we're
72 /// compiled without the `atomics` feature) so we "safely lie" and say our
73 /// `Rc` pointer is good enough.
74 ///
75 /// The implementation is based off of futures::task::ArcWake
76unsafe fn into_raw_waker(this: Rc<Self>) -> RawWaker {
77unsafe fn raw_clone(ptr: *const ()) -> RawWaker {
78let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
79 Task::into_raw_waker(Rc::clone(&ptr))
80 }
8182unsafe fn raw_wake(ptr: *const ()) {
83let ptr = Rc::from_raw(ptr as *const Task);
84 Task::wake(ptr);
85 }
8687unsafe fn raw_wake_by_ref(ptr: *const ()) {
88let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
89 Task::wake_by_ref(&ptr);
90 }
9192unsafe fn raw_drop(ptr: *const ()) {
93 drop(Rc::from_raw(ptr as *const Task));
94 }
9596static VTABLE: RawWakerVTable =
97 RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
9899 RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE)
100 }
101102pub(crate) fn run(&self) {
103let mut borrow = self.inner.borrow_mut();
104105// Wakeups can come in after a Future has finished and been destroyed,
106 // so handle this gracefully by just ignoring the request to run.
107let inner = match borrow.as_mut() {
108Some(inner) => inner,
109None => return,
110 };
111112// Ensure that if poll calls `waker.wake()` we can get enqueued back on
113 // the run queue.
114self.is_queued.set(false);
115116let poll = {
117let mut cx = Context::from_waker(&inner.waker);
118 inner.future.as_mut().poll(&mut cx)
119 };
120121// If a future has finished (`Ready`) then clean up resources associated
122 // with the future ASAP. This ensures that we don't keep anything extra
123 // alive in-memory by accident. Our own struct, `Rc<Task>` won't
124 // actually go away until all wakers referencing us go away, which may
125 // take quite some time, so ensure that the heaviest of resources are
126 // released early.
127if poll.is_ready() {
128*borrow = None;
129 }
130 }
131}