use std::cell::{Cell, RefCell}; use std::future::Future; use std::mem::ManuallyDrop; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; struct Inner { future: Pin + 'static>>, waker: Waker, } pub(crate) struct Task { // The actual Future that we're executing as part of this task. // // This is an Option so that the Future can be immediately dropped when it's // finished inner: RefCell>, // This is used to ensure that the Task will only be queued once is_queued: Cell, } impl Task { pub(crate) fn spawn(future: Pin + 'static>>) { let this = Rc::new(Self { inner: RefCell::new(None), is_queued: Cell::new(false), }); let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) }; *this.inner.borrow_mut() = Some(Inner { future, waker }); Task::wake_by_ref(&this); } fn wake_by_ref(this: &Rc) { // If we've already been placed on the run queue then there's no need to // requeue ourselves since we're going to run at some point in the // future anyway. if this.is_queued.replace(true) { return; } crate::queue::QUEUE.with(|queue| { queue.push_task(Rc::clone(this)); }); } /// Creates a standard library `RawWaker` from an `Rc` of ourselves. /// /// Note that in general this is wildly unsafe because everything with /// Futures requires `Sync` + `Send` with regard to Wakers. For wasm, /// however, everything is guaranteed to be singlethreaded (since we're /// compiled without the `atomics` feature) so we "safely lie" and say our /// `Rc` pointer is good enough. unsafe fn into_raw_waker(this: Rc) -> RawWaker { unsafe fn raw_clone(ptr: *const ()) -> RawWaker { let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task)); Task::into_raw_waker((*ptr).clone()) } unsafe fn raw_wake(ptr: *const ()) { let ptr = Rc::from_raw(ptr as *const Task); Task::wake_by_ref(&ptr); } unsafe fn raw_wake_by_ref(ptr: *const ()) { let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task)); Task::wake_by_ref(&ptr); } unsafe fn raw_drop(ptr: *const ()) { drop(Rc::from_raw(ptr as *const Task)); } const VTABLE: RawWakerVTable = RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop); RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE) } pub(crate) fn run(&self) { let mut borrow = self.inner.borrow_mut(); // Wakeups can come in after a Future has finished and been destroyed, // so handle this gracefully by just ignoring the request to run. let inner = match borrow.as_mut() { Some(inner) => inner, None => return, }; // Ensure that if poll calls `waker.wake()` we can get enqueued back on // the run queue. self.is_queued.set(false); let poll = { let mut cx = Context::from_waker(&inner.waker); inner.future.as_mut().poll(&mut cx) }; // If a future has finished (`Ready`) then clean up resources associated // with the future ASAP. This ensures that we don't keep anything extra // alive in-memory by accident. Our own struct, `Rc` won't // actually go away until all wakers referencing us go away, which may // take quite some time, so ensure that the heaviest of resources are // released early. if let Poll::Ready(_) = poll { *borrow = None; } } }