js_sys/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(thread_local_v2, js_namespace = console, js_name = createTask)]
27 static CREATE_TASK: Option<crate::Function<fn(crate::JsString) -> ConsoleTask>>;
28
29 #[wasm_bindgen(method)]
30 fn run(this: &ConsoleTask, poll: &mut dyn FnMut() -> bool) -> bool;
31}
32
33#[cfg(debug_assertions)]
34fn try_create_task(name: &str) -> Option<ConsoleTask> {
35 CREATE_TASK.with(|create_task| {
36 create_task.as_ref().and_then(|f| {
37 f.call(&wasm_bindgen::JsValue::UNDEFINED, (&name.into(),))
38 .ok()
39 })
40 })
41}
42
43pub(crate) struct Task {
44 #[cfg(debug_assertions)]
47 console: Option<ConsoleTask>,
48
49 inner: RefCell<Option<Inner>>,
54
55 is_queued: Cell<bool>,
57}
58
59impl Task {
60 pub(crate) fn spawn<F: Future<Output = ()> + 'static>(future: F) {
61 let this = Rc::new(Self {
62 #[cfg(debug_assertions)]
63 console: try_create_task(core::any::type_name::<F>()),
64 inner: RefCell::new(None),
65 is_queued: Cell::new(true),
66 });
67
68 let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) };
69
70 *this.inner.borrow_mut() = Some(Inner {
71 future: Box::pin(future),
72 waker,
73 });
74
75 crate::futures::queue::Queue::with(|queue| queue.schedule_task(this));
76 }
77
78 fn force_wake(this: Rc<Self>) {
79 crate::futures::queue::Queue::with(|queue| {
80 queue.push_task(this);
81 });
82 }
83
84 fn wake(this: Rc<Self>) {
85 if this.is_queued.replace(true) {
89 return;
90 }
91
92 Self::force_wake(this);
93 }
94
95 fn wake_by_ref(this: &Rc<Self>) {
96 if this.is_queued.replace(true) {
100 return;
101 }
102
103 Self::force_wake(Rc::clone(this));
104 }
105
106 unsafe fn into_raw_waker(this: Rc<Self>) -> RawWaker {
116 unsafe fn raw_clone(ptr: *const ()) -> RawWaker {
117 let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
118 Task::into_raw_waker(Rc::clone(&ptr))
119 }
120
121 unsafe fn raw_wake(ptr: *const ()) {
122 let ptr = Rc::from_raw(ptr as *const Task);
123 Task::wake(ptr);
124 }
125
126 unsafe fn raw_wake_by_ref(ptr: *const ()) {
127 let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
128 Task::wake_by_ref(&ptr);
129 }
130
131 unsafe fn raw_drop(ptr: *const ()) {
132 drop(Rc::from_raw(ptr as *const Task));
133 }
134
135 static VTABLE: RawWakerVTable =
136 RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
137
138 RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE)
139 }
140
141 pub(crate) fn run(&self) {
142 let mut borrow = self.inner.borrow_mut();
143
144 let inner = match borrow.as_mut() {
147 Some(inner) => inner,
148 None => return,
149 };
150
151 self.is_queued.set(false);
154
155 #[cfg(debug_assertions)]
158 let is_ready = match self.console.as_ref() {
159 Some(console) => {
163 let mut inner = core::panic::AssertUnwindSafe(inner);
164 console.run(&mut move || inner.is_ready())
165 }
166 None => inner.is_ready(),
167 };
168
169 #[cfg(not(debug_assertions))]
172 let is_ready = inner.is_ready();
173
174 if is_ready {
181 *borrow = None;
182 }
183 }
184}