agnostic_lite/wasm/
sleep.rs

1use core::{
2  future::Future,
3  pin::Pin,
4  task::{Context, Poll},
5  time::Duration,
6};
7use std::time::Instant;
8use wasm::Delay;
9
10use crate::time::{AsyncLocalSleep, AsyncLocalSleepExt};
11
12pin_project_lite::pin_project! {
13  /// The [`AsyncSleep`] implementation for wasm-bindgen based runtime.
14  #[cfg_attr(docsrs, doc(cfg(all(feature = "std", feature = "wasm"))))]
15  pub struct WasmSleep {
16    #[pin]
17    pub(crate) sleep: Delay,
18    pub(crate) ddl: Instant,
19    pub(crate) duration: Duration,
20  }
21}
22
23impl Future for WasmSleep {
24  type Output = Instant;
25
26  fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
27    let ddl = self.ddl;
28    self.project().sleep.poll(cx).map(|_| ddl)
29  }
30}
31
32impl AsyncLocalSleep for WasmSleep {
33  type Instant = Instant;
34
35  fn reset(self: Pin<&mut Self>, deadline: Instant) {
36    let mut this = self.project();
37    let ddl = deadline - Instant::now();
38    this.sleep.reset(ddl);
39    *this.ddl = deadline;
40  }
41}
42
43impl AsyncLocalSleepExt for WasmSleep {
44  fn sleep_local(after: Duration) -> Self
45  where
46    Self: Sized,
47  {
48    Self {
49      ddl: Instant::now() + after,
50      sleep: Delay::new(after),
51      duration: after,
52    }
53  }
54
55  fn sleep_local_until(deadline: Instant) -> Self
56  where
57    Self: Sized,
58  {
59    let duration = deadline - Instant::now();
60    Self {
61      sleep: Delay::new(duration),
62      ddl: deadline,
63      duration,
64    }
65  }
66}
67
68#[cfg(test)]
69mod tests {
70  use super::WasmSleep;
71  use crate::time::{AsyncSleep, AsyncSleepExt};
72  use core::pin::Pin;
73  use std::time::{Duration, Instant};
74
75  const ORIGINAL: Duration = Duration::from_secs(1);
76  const RESET: Duration = Duration::from_secs(2);
77  const BOUND: Duration = Duration::from_millis(10);
78
79  #[test]
80  fn test_wasm_sleep() {
81    futures::executor::block_on(async {
82      let start = Instant::now();
83      let sleep = WasmSleep::sleep(ORIGINAL);
84      let ins = sleep.await;
85      assert!(ins >= start + ORIGINAL);
86      let elapsed = start.elapsed();
87      assert!(elapsed >= ORIGINAL && elapsed < ORIGINAL + BOUND);
88    });
89  }
90
91  #[test]
92  fn test_wasm_sleep_until() {
93    futures::executor::block_on(async {
94      let start = Instant::now();
95      let sleep = WasmSleep::sleep_until(start + ORIGINAL);
96      let ins = sleep.await;
97      assert!(ins >= start + ORIGINAL);
98      let elapsed = start.elapsed();
99      assert!(elapsed >= ORIGINAL && elapsed < ORIGINAL + BOUND);
100    });
101  }
102
103  #[test]
104  fn test_wasm_sleep_reset() {
105    futures::executor::block_on(async {
106      let start = Instant::now();
107      let mut sleep = WasmSleep::sleep(ORIGINAL);
108      let pin = Pin::new(&mut sleep);
109      pin.reset(Instant::now() + RESET);
110      let ins = sleep.await;
111      assert!(ins >= start + RESET);
112      let elapsed = start.elapsed();
113      assert!(elapsed >= RESET && elapsed < RESET + BOUND);
114    });
115  }
116
117  #[test]
118  fn test_wasm_sleep_reset2() {
119    futures::executor::block_on(async {
120      let start = Instant::now();
121      let mut sleep = WasmSleep::sleep_until(start + ORIGINAL);
122      let pin = Pin::new(&mut sleep);
123      pin.reset(Instant::now() + RESET);
124      let ins = sleep.await;
125      assert!(ins >= start + RESET);
126      let elapsed = start.elapsed();
127      assert!(elapsed >= RESET && elapsed < RESET + BOUND);
128    });
129  }
130}