Skip to main content

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::LocalRuntimeLite for WasmRuntime {
197  type LocalSpawner = WasmSpawner;
198  type BlockingSpawner = WasmSpawner;
199
200  cfg_time!(
201    type Instant = Instant;
202
203    type LocalInterval = WasmInterval;
204
205    type LocalSleep = WasmSleep;
206
207    type LocalDelay<F>
208      = WasmDelay<F>
209    where
210      F: Future;
211
212    type LocalTimeout<F>
213      = WasmTimeout<F>
214    where
215      F: Future;
216  );
217
218  fn new() -> Self {
219    Self
220  }
221
222  fn name() -> &'static str {
223    "wasm-bindgen-futures"
224  }
225
226  fn fqname() -> &'static str {
227    "wasm-bindgen-futures"
228  }
229
230  fn block_on<F: Future>(_f: F) -> F::Output {
231    panic!("LocalRuntimeLite::block_on is not supported on wasm")
232  }
233
234  cfg_time!(
235    fn interval_local(interval: Duration) -> Self::LocalInterval {
236      use crate::time::AsyncIntervalExt;
237
238      WasmInterval::interval(interval)
239    }
240
241    fn interval_local_at(start: Instant, period: Duration) -> Self::LocalInterval {
242      use crate::time::AsyncIntervalExt;
243
244      WasmInterval::interval_at(start, period)
245    }
246
247    fn sleep_local(duration: Duration) -> Self::LocalSleep {
248      use crate::time::AsyncSleepExt;
249
250      WasmSleep::sleep(duration)
251    }
252
253    fn sleep_local_until(instant: Instant) -> Self::LocalSleep {
254      use crate::time::AsyncSleepExt;
255
256      WasmSleep::sleep_until(instant)
257    }
258
259    fn delay_local<F>(duration: Duration, fut: F) -> Self::LocalDelay<F>
260    where
261      F: Future,
262    {
263      use crate::time::AsyncLocalDelayExt;
264
265      <WasmDelay<F> as AsyncLocalDelayExt<F>>::delay(duration, fut)
266    }
267
268    fn delay_local_at<F>(deadline: Instant, fut: F) -> Self::LocalDelay<F>
269    where
270      F: Future,
271    {
272      use crate::time::AsyncLocalDelayExt;
273
274      <WasmDelay<F> as AsyncLocalDelayExt<F>>::delay_at(deadline, fut)
275    }
276
277    fn timeout_local<F>(duration: Duration, future: F) -> Self::LocalTimeout<F>
278    where
279      F: Future,
280    {
281      use crate::time::AsyncLocalTimeout;
282
283      <WasmTimeout<F> as AsyncLocalTimeout<F>>::timeout_local(duration, future)
284    }
285
286    fn timeout_local_at<F>(deadline: Instant, future: F) -> Self::LocalTimeout<F>
287    where
288      F: Future,
289    {
290      use crate::time::AsyncLocalTimeout;
291
292      <WasmTimeout<F> as AsyncLocalTimeout<F>>::timeout_local_at(deadline, future)
293    }
294  );
295}
296
297impl super::RuntimeLite for WasmRuntime {
298  type Spawner = WasmSpawner;
299
300  cfg_time!(
301    type AfterSpawner = WasmSpawner;
302
303    type Interval = WasmInterval;
304
305    type Sleep = WasmSleep;
306
307    type Delay<F>
308      = WasmDelay<F>
309    where
310      F: Future + Send;
311
312    type Timeout<F>
313      = WasmTimeout<F>
314    where
315      F: Future + Send;
316  );
317
318  async fn yield_now() {
319    YieldNow(false).await
320  }
321
322  cfg_time!(
323    fn interval(interval: Duration) -> Self::Interval {
324      use crate::time::AsyncIntervalExt;
325
326      WasmInterval::interval(interval)
327    }
328
329    fn interval_at(start: Instant, period: Duration) -> Self::Interval {
330      use crate::time::AsyncIntervalExt;
331
332      WasmInterval::interval_at(start, period)
333    }
334
335    fn sleep(duration: Duration) -> Self::Sleep {
336      use crate::time::AsyncSleepExt;
337
338      WasmSleep::sleep(duration)
339    }
340
341    fn sleep_until(instant: Instant) -> Self::Sleep {
342      use crate::time::AsyncSleepExt;
343
344      WasmSleep::sleep_until(instant)
345    }
346
347    fn delay<F>(duration: Duration, fut: F) -> Self::Delay<F>
348    where
349      F: Future + Send,
350    {
351      use crate::time::AsyncDelayExt;
352
353      <WasmDelay<F> as AsyncDelayExt<F>>::delay(duration, fut)
354    }
355
356    fn delay_at<F>(deadline: Instant, fut: F) -> Self::Delay<F>
357    where
358      F: Future + Send,
359    {
360      use crate::time::AsyncDelayExt;
361
362      <WasmDelay<F> as AsyncDelayExt<F>>::delay_at(deadline, fut)
363    }
364
365    fn timeout<F>(duration: Duration, future: F) -> Self::Timeout<F>
366    where
367      F: Future + Send,
368    {
369      use crate::time::AsyncTimeout;
370
371      <WasmTimeout<F> as AsyncTimeout<F>>::timeout(duration, future)
372    }
373
374    fn timeout_at<F>(deadline: Instant, future: F) -> Self::Timeout<F>
375    where
376      F: Future + Send,
377    {
378      use crate::time::AsyncTimeout;
379
380      <WasmTimeout<F> as AsyncTimeout<F>>::timeout_at(deadline, future)
381    }
382  );
383}