agnostic_lite/
wasm.rs

1cfg_time!(
2  mod after;
3  mod delay;
4  mod interval;
5  mod sleep;
6  mod timeout;
7
8  pub use after::*;
9  pub use delay::*;
10  pub use interval::*;
11  pub use sleep::*;
12  pub use timeout::*;
13
14  use std::time::{Duration, Instant};
15);
16
17use core::{
18  future::Future,
19  pin::Pin,
20  task::{Context, Poll},
21};
22
23use wasm::channel::*;
24
25use super::handle::JoinError;
26use crate::{AsyncBlockingSpawner, AsyncLocalSpawner, AsyncSpawner, Yielder};
27
28/// The join handle returned by [`WasmSpawner`].
29pub struct JoinHandle<F> {
30  pub(crate) stop_tx: oneshot::Sender<bool>,
31  pub(crate) rx: oneshot::Receiver<F>,
32}
33
34impl<F> Future for JoinHandle<F> {
35  type Output = Result<F, JoinError>;
36
37  fn poll(
38    mut self: core::pin::Pin<&mut Self>,
39    cx: &mut core::task::Context<'_>,
40  ) -> core::task::Poll<Self::Output> {
41    core::pin::Pin::new(&mut self.rx)
42      .poll(cx)
43      .map(|res| res.map_err(|_| JoinError::new()))
44  }
45}
46
47impl<F> JoinHandle<F> {
48  /// Detach the future from the spawner.
49  #[inline]
50  pub fn detach(self) {
51    let _ = self.stop_tx.send(false);
52  }
53
54  /// Cancel the future.
55  #[inline]
56  pub fn cancel(self) {
57    let _ = self.stop_tx.send(true);
58  }
59}
60
61impl<O> super::JoinHandle<O> for JoinHandle<O> {
62  type JoinError = JoinError;
63
64  fn detach(self) {
65    Self::detach(self)
66  }
67
68  fn abort(self) {
69    self.cancel();
70  }
71}
72
73impl<O> super::LocalJoinHandle<O> for JoinHandle<O> {
74  type JoinError = JoinError;
75
76  fn detach(self) {
77    Self::detach(self)
78  }
79}
80
81/// A [`AsyncSpawner`] that uses the [`wasm-bindgen-futures`] runtime.
82///
83/// [`wasm-bindgen-futures`]: https://docs.rs/wasm-bindgen-futures
84#[derive(Debug, Clone, Copy)]
85pub struct WasmSpawner;
86
87impl AsyncSpawner for WasmSpawner {
88  type JoinHandle<F>
89    = JoinHandle<F>
90  where
91    F: Send + 'static;
92
93  fn spawn<F>(future: F) -> Self::JoinHandle<F::Output>
94  where
95    F::Output: Send + 'static,
96    F: core::future::Future + Send + 'static,
97  {
98    <Self as super::AsyncLocalSpawner>::spawn_local(future)
99  }
100}
101
102impl AsyncLocalSpawner for WasmSpawner {
103  type JoinHandle<F>
104    = JoinHandle<F>
105  where
106    F: 'static;
107
108  fn spawn_local<F>(future: F) -> Self::JoinHandle<F::Output>
109  where
110    F::Output: 'static,
111    F: core::future::Future + 'static,
112  {
113    use futures_util::FutureExt;
114
115    let (tx, rx) = oneshot::channel();
116    let (stop_tx, stop_rx) = oneshot::channel();
117    wasm::spawn_local(async {
118      futures_util::pin_mut!(future);
119
120      futures_util::select! {
121        sig = stop_rx.fuse() => {
122          match sig {
123            Ok(true) => {
124              // if we receive a stop signal, we just stop this task.
125            },
126            Ok(false) | Err(_) => {
127              let _ = future.await;
128            },
129          }
130        },
131        future = (&mut future).fuse() => {
132          let _ = tx.send(future);
133        }
134      }
135    });
136    JoinHandle { stop_tx, rx }
137  }
138}
139
140impl AsyncBlockingSpawner for WasmSpawner {
141  type JoinHandle<R>
142    = JoinHandle<R>
143  where
144    R: Send + 'static;
145
146  fn spawn_blocking<F, R>(_: F) -> Self::JoinHandle<R>
147  where
148    F: FnOnce() -> R + Send + 'static,
149    R: Send + 'static,
150  {
151    panic!("wasm-bindgen-futures does not support blocking tasks")
152  }
153}
154
155impl Yielder for WasmSpawner {
156  async fn yield_now() {
157    YieldNow(false).await
158  }
159
160  async fn yield_now_local() {
161    YieldNow(false).await
162  }
163}
164
165/// Future for the [`yield_now`](RuntimeLite::yield_now) function.
166#[derive(Debug)]
167#[must_use = "futures do nothing unless you `.await` or poll them"]
168struct YieldNow(bool);
169
170impl Future for YieldNow {
171  type Output = ();
172
173  fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
174    if !self.0 {
175      self.0 = true;
176      cx.waker().wake_by_ref();
177      Poll::Pending
178    } else {
179      Poll::Ready(())
180    }
181  }
182}
183
184/// Concrete [`RuntimeLite`](crate::RuntimeLite) implementation based on [`wasm-bindgen-futures`] runtime.
185///
186/// [`wasm-bindgen-futures`]: https://docs.rs/wasm-bindgen-futures
187#[derive(Debug, Clone, Copy)]
188pub struct WasmRuntime;
189
190impl core::fmt::Display for WasmRuntime {
191  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
192    write!(f, "wasm-bindgen-futures")
193  }
194}
195
196impl super::RuntimeLite for WasmRuntime {
197  type Spawner = WasmSpawner;
198  type LocalSpawner = WasmSpawner;
199  type BlockingSpawner = WasmSpawner;
200
201  cfg_time!(
202    type Instant = Instant;
203    type AfterSpawner = WasmSpawner;
204
205    type Interval = WasmInterval;
206
207    type LocalInterval = WasmInterval;
208
209    type Sleep = WasmSleep;
210
211    type LocalSleep = WasmSleep;
212
213    type Delay<F>
214      = WasmDelay<F>
215    where
216      F: Future + Send;
217
218    type LocalDelay<F>
219      = WasmDelay<F>
220    where
221      F: Future;
222
223    type Timeout<F>
224      = WasmTimeout<F>
225    where
226      F: Future + Send;
227
228    type LocalTimeout<F>
229      = WasmTimeout<F>
230    where
231      F: Future;
232  );
233
234  fn new() -> Self {
235    Self
236  }
237
238  fn name() -> &'static str {
239    "wasm-bindgen-futures"
240  }
241
242  fn fqname() -> &'static str {
243    "wasm-bindgen-futures"
244  }
245
246  fn block_on<F: Future>(_f: F) -> F::Output {
247    panic!("RuntimeLite::block_on is not supported on wasm")
248  }
249
250  async fn yield_now() {
251    YieldNow(false).await
252  }
253
254  cfg_time!(
255    fn interval(interval: Duration) -> Self::Interval {
256      use crate::time::AsyncIntervalExt;
257
258      WasmInterval::interval(interval)
259    }
260
261    fn interval_at(start: Instant, period: Duration) -> Self::Interval {
262      use crate::time::AsyncIntervalExt;
263
264      WasmInterval::interval_at(start, period)
265    }
266
267    fn interval_local(interval: Duration) -> Self::LocalInterval {
268      use crate::time::AsyncIntervalExt;
269
270      WasmInterval::interval(interval)
271    }
272
273    fn interval_local_at(start: Instant, period: Duration) -> Self::LocalInterval {
274      use crate::time::AsyncIntervalExt;
275
276      WasmInterval::interval_at(start, period)
277    }
278
279    fn sleep(duration: Duration) -> Self::Sleep {
280      use crate::time::AsyncSleepExt;
281
282      WasmSleep::sleep(duration)
283    }
284
285    fn sleep_until(instant: Instant) -> Self::Sleep {
286      use crate::time::AsyncSleepExt;
287
288      WasmSleep::sleep_until(instant)
289    }
290
291    fn sleep_local(duration: Duration) -> Self::LocalSleep {
292      use crate::time::AsyncSleepExt;
293
294      WasmSleep::sleep(duration)
295    }
296
297    fn sleep_local_until(instant: Instant) -> Self::LocalSleep {
298      use crate::time::AsyncSleepExt;
299
300      WasmSleep::sleep_until(instant)
301    }
302
303    fn delay<F>(duration: Duration, fut: F) -> Self::Delay<F>
304    where
305      F: Future + Send,
306    {
307      use crate::time::AsyncDelayExt;
308
309      <WasmDelay<F> as AsyncDelayExt<F>>::delay(duration, fut)
310    }
311
312    fn delay_local<F>(duration: Duration, fut: F) -> Self::LocalDelay<F>
313    where
314      F: Future,
315    {
316      use crate::time::AsyncLocalDelayExt;
317
318      <WasmDelay<F> as AsyncLocalDelayExt<F>>::delay(duration, fut)
319    }
320
321    fn delay_at<F>(deadline: Instant, fut: F) -> Self::Delay<F>
322    where
323      F: Future + Send,
324    {
325      use crate::time::AsyncDelayExt;
326
327      <WasmDelay<F> as AsyncDelayExt<F>>::delay_at(deadline, fut)
328    }
329
330    fn delay_local_at<F>(deadline: Instant, fut: F) -> Self::LocalDelay<F>
331    where
332      F: Future,
333    {
334      use crate::time::AsyncLocalDelayExt;
335
336      <WasmDelay<F> as AsyncLocalDelayExt<F>>::delay_at(deadline, fut)
337    }
338
339    fn timeout<F>(duration: Duration, future: F) -> Self::Timeout<F>
340    where
341      F: Future + Send,
342    {
343      use crate::time::AsyncTimeout;
344
345      <WasmTimeout<F> as AsyncTimeout<F>>::timeout(duration, future)
346    }
347
348    fn timeout_at<F>(deadline: Instant, future: F) -> Self::Timeout<F>
349    where
350      F: Future + Send,
351    {
352      use crate::time::AsyncTimeout;
353
354      <WasmTimeout<F> as AsyncTimeout<F>>::timeout_at(deadline, future)
355    }
356
357    fn timeout_local<F>(duration: Duration, future: F) -> Self::LocalTimeout<F>
358    where
359      F: Future,
360    {
361      use crate::time::AsyncLocalTimeout;
362
363      <WasmTimeout<F> as AsyncLocalTimeout<F>>::timeout_local(duration, future)
364    }
365
366    fn timeout_local_at<F>(deadline: Instant, future: F) -> Self::LocalTimeout<F>
367    where
368      F: Future,
369    {
370      use crate::time::AsyncLocalTimeout;
371
372      <WasmTimeout<F> as AsyncLocalTimeout<F>>::timeout_local_at(deadline, future)
373    }
374  );
375}