wasm_bindgen_futures/task/
singlethread.rs1use 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};
8
9struct Inner {
10 future: Pin<Box<dyn Future<Output = ()> + 'static>>,
11 waker: Waker,
12}
13
14impl Inner {
15 fn is_ready(&mut self) -> bool {
16 let mut cx = Context::from_waker(&self.waker);
17 self.future.as_mut().poll(&mut cx).is_ready()
18 }
19}
20
21#[cfg(debug_assertions)]
22#[wasm_bindgen::prelude::wasm_bindgen]
23extern "C" {
24 type ConsoleTask;
25
26 #[wasm_bindgen(js_namespace = console, js_name = createTask, catch)]
27 fn create_task(name: &str) -> Result<ConsoleTask, wasm_bindgen::JsValue>;
28
29 #[wasm_bindgen(method)]
30 fn run(this: &ConsoleTask, poll: &mut dyn FnMut() -> bool) -> bool;
31}
32
33pub(crate) struct Task {
34 #[cfg(debug_assertions)]
37 console: Option<ConsoleTask>,
38
39 inner: RefCell<Option<Inner>>,
44
45 is_queued: Cell<bool>,
47}
48
49impl Task {
50 pub(crate) fn spawn<F: Future<Output = ()> + 'static>(future: F) {
51 let this = Rc::new(Self {
52 #[cfg(debug_assertions)]
53 console: create_task(core::any::type_name::<F>()).ok(),
54 inner: RefCell::new(None),
55 is_queued: Cell::new(true),
56 });
57
58 let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) };
59
60 *this.inner.borrow_mut() = Some(Inner {
61 future: Box::pin(future),
62 waker,
63 });
64
65 crate::queue::Queue::with(|queue| queue.schedule_task(this));
66 }
67
68 fn force_wake(this: Rc<Self>) {
69 crate::queue::Queue::with(|queue| {
70 queue.push_task(this);
71 });
72 }
73
74 fn wake(this: Rc<Self>) {
75 if this.is_queued.replace(true) {
79 return;
80 }
81
82 Self::force_wake(this);
83 }
84
85 fn wake_by_ref(this: &Rc<Self>) {
86 if this.is_queued.replace(true) {
90 return;
91 }
92
93 Self::force_wake(Rc::clone(this));
94 }
95
96 unsafe fn into_raw_waker(this: Rc<Self>) -> RawWaker {
106 unsafe fn raw_clone(ptr: *const ()) -> RawWaker {
107 let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
108 Task::into_raw_waker(Rc::clone(&ptr))
109 }
110
111 unsafe fn raw_wake(ptr: *const ()) {
112 let ptr = Rc::from_raw(ptr as *const Task);
113 Task::wake(ptr);
114 }
115
116 unsafe fn raw_wake_by_ref(ptr: *const ()) {
117 let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
118 Task::wake_by_ref(&ptr);
119 }
120
121 unsafe fn raw_drop(ptr: *const ()) {
122 drop(Rc::from_raw(ptr as *const Task));
123 }
124
125 static VTABLE: RawWakerVTable =
126 RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
127
128 RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE)
129 }
130
131 pub(crate) fn run(&self) {
132 let mut borrow = self.inner.borrow_mut();
133
134 let inner = match borrow.as_mut() {
137 Some(inner) => inner,
138 None => return,
139 };
140
141 self.is_queued.set(false);
144
145 #[cfg(debug_assertions)]
148 let is_ready = match self.console.as_ref() {
149 Some(console) => console.run(&mut move || inner.is_ready()),
150 None => inner.is_ready(),
151 };
152
153 #[cfg(not(debug_assertions))]
156 let is_ready = inner.is_ready();
157
158 if is_ready {
165 *borrow = None;
166 }
167 }
168}