bitwarden_threading/
time.rs1use std::time::Duration;
2
3#[cfg(not(target_arch = "wasm32"))]
4pub async fn sleep(duration: Duration) {
5 tokio::time::sleep(duration).await;
6}
7
8#[cfg(target_arch = "wasm32")]
9pub async fn sleep(duration: Duration) {
10 use gloo_timers::future::sleep;
11
12 sleep(duration).await;
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct ElapsedError;
18
19impl std::fmt::Display for ElapsedError {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.write_str("operation timed out")
22 }
23}
24
25impl std::error::Error for ElapsedError {}
26
27#[cfg(not(target_arch = "wasm32"))]
28pub async fn timeout<F: std::future::Future>(
29 duration: Duration,
30 future: F,
31) -> Result<F::Output, ElapsedError> {
32 tokio::time::timeout(duration, future)
33 .await
34 .map_err(|_| ElapsedError)
35}
36
37#[cfg(target_arch = "wasm32")]
38pub async fn timeout<F: std::future::Future>(
39 duration: Duration,
40 future: F,
41) -> Result<F::Output, ElapsedError> {
42 let (tx, rx) = tokio::sync::oneshot::channel();
43 wasm_bindgen_futures::spawn_local(async move {
44 sleep(duration).await;
45 let _ = tx.send(());
46 });
47
48 tokio::pin!(future);
49 tokio::select! {
50 result = &mut future => Ok(result),
51 _ = rx => Err(ElapsedError),
52 }
53}
54
55#[cfg(test)]
56mod test {
57 use wasm_bindgen_test::wasm_bindgen_test;
58
59 #[wasm_bindgen_test]
60 #[allow(dead_code)] async fn should_sleep_wasm() {
62 use js_sys::Date;
63
64 use super::*;
65
66 console_error_panic_hook::set_once();
67 let start = Date::now();
68
69 sleep(Duration::from_millis(100)).await;
70
71 let end = Date::now();
72 let elapsed = end - start;
73
74 assert!(elapsed >= 90.0, "Elapsed time was less than expected");
75 }
76
77 #[tokio::test]
78 async fn should_sleep_tokio() {
79 use std::time::Instant;
80
81 use super::*;
82
83 let start = Instant::now();
84
85 sleep(Duration::from_millis(100)).await;
86
87 let end = Instant::now();
88 let elapsed = end.duration_since(start);
89
90 assert!(
91 elapsed >= Duration::from_millis(90),
92 "Elapsed time was less than expected"
93 );
94 }
95
96 #[tokio::test]
97 async fn timeout_returns_value_when_future_completes_first() {
98 use std::time::Duration;
99
100 use super::timeout;
101
102 let result = timeout(Duration::from_secs(5), async { 42 }).await;
103 assert_eq!(result, Ok(42));
104 }
105
106 #[tokio::test(flavor = "current_thread", start_paused = true)]
107 async fn timeout_returns_elapsed_when_sleep_first() {
108 use std::time::Duration;
109
110 use super::{ElapsedError, timeout};
111
112 let result = timeout(
113 Duration::from_millis(10),
114 tokio::time::sleep(Duration::from_secs(60)),
115 )
116 .await;
117 assert_eq!(result, Err(ElapsedError));
118 }
119}