firebase_rs_sdk/platform/
runtime.rs1use std::fmt;
2use std::future::Future;
3use std::time::{Duration, SystemTime};
4
5#[cfg(all(target_arch = "wasm32", feature = "wasm-web"))]
6use js_sys::Date;
7#[cfg(all(target_arch = "wasm32", feature = "wasm-web"))]
8use std::time::UNIX_EPOCH;
9
10#[cfg(target_arch = "wasm32")]
12pub fn spawn_detached<F>(future: F)
13where
14 F: Future<Output = ()> + 'static,
15{
16 wasm_bindgen_futures::spawn_local(future);
17}
18
19#[cfg(not(target_arch = "wasm32"))]
21pub fn spawn_detached<F>(future: F)
22where
23 F: Future<Output = ()> + Send + 'static,
24{
25 use std::sync::LazyLock;
26 use tokio::runtime::{Builder, Handle, Runtime};
27
28 static BACKGROUND_RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
29 Builder::new_current_thread()
30 .enable_all()
31 .build()
32 .expect("failed to build background tokio runtime")
33 });
34
35 if let Ok(handle) = Handle::try_current() {
36 handle.spawn(future);
37 } else {
38 let _ = BACKGROUND_RUNTIME.spawn(future);
39 }
40}
41
42#[cfg(all(target_arch = "wasm32", feature = "wasm-web"))]
44pub fn now() -> SystemTime {
45 let millis = Date::now() as u64;
46 UNIX_EPOCH + Duration::from_millis(millis)
47}
48
49#[cfg(not(all(target_arch = "wasm32", feature = "wasm-web")))]
51pub fn now() -> SystemTime {
52 SystemTime::now()
53}
54
55pub async fn sleep(duration: Duration) {
57 if duration.is_zero() {
58 return;
59 }
60
61 sleep_impl(duration).await;
62}
63
64pub async fn yield_now() {
66 yield_now_impl().await;
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub struct TimeoutError;
72
73impl fmt::Display for TimeoutError {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "operation timed out")
76 }
77}
78
79impl std::error::Error for TimeoutError {}
80
81pub async fn with_timeout<F, T>(future: F, duration: Duration) -> Result<T, TimeoutError>
84where
85 F: Future<Output = T>,
86{
87 if duration.is_zero() {
88 return Ok(future.await);
89 }
90
91 with_timeout_impl(future, duration).await
92}
93
94#[cfg(target_arch = "wasm32")]
95async fn sleep_impl(duration: Duration) {
96 use gloo_timers::future::sleep;
97 sleep(duration).await;
98}
99
100#[cfg(not(target_arch = "wasm32"))]
101async fn sleep_impl(duration: Duration) {
102 use tokio::time::sleep;
103 sleep(duration).await;
104}
105
106#[cfg(target_arch = "wasm32")]
107async fn yield_now_impl() {
108 use std::future::Future;
109 use std::pin::Pin;
110 use std::task::{Context, Poll};
111
112 struct YieldOnce {
113 yielded: bool,
114 }
115
116 impl Future for YieldOnce {
117 type Output = ();
118
119 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
120 if self.yielded {
121 Poll::Ready(())
122 } else {
123 self.yielded = true;
124 cx.waker().wake_by_ref();
125 Poll::Pending
126 }
127 }
128 }
129
130 YieldOnce { yielded: false }.await;
131}
132
133#[cfg(not(target_arch = "wasm32"))]
134async fn yield_now_impl() {
135 tokio::task::yield_now().await;
136}
137
138#[cfg(not(target_arch = "wasm32"))]
139async fn with_timeout_impl<F, T>(future: F, duration: Duration) -> Result<T, TimeoutError>
140where
141 F: Future<Output = T>,
142{
143 use tokio::time::timeout;
144
145 timeout(duration, future).await.map_err(|_| TimeoutError)
146}
147
148#[cfg(target_arch = "wasm32")]
149async fn with_timeout_impl<F, T>(future: F, duration: Duration) -> Result<T, TimeoutError>
150where
151 F: Future<Output = T>,
152{
153 use futures::future::poll_fn;
154 use gloo_timers::future::TimeoutFuture;
155 use std::future::Future;
156
157 let mut future = Box::pin(future);
158 let timeout_ms = duration.as_millis().min(u32::MAX as u128) as u32;
159 let timeout_ms = timeout_ms.max(1);
160 let mut timeout_future = Box::pin(TimeoutFuture::new(timeout_ms));
161
162 poll_fn(|cx| {
163 if let std::task::Poll::Ready(result) = future.as_mut().poll(cx) {
164 return std::task::Poll::Ready(Ok(result));
165 }
166
167 if let std::task::Poll::Ready(_) = timeout_future.as_mut().poll(cx) {
168 return std::task::Poll::Ready(Err(TimeoutError));
169 }
170
171 std::task::Poll::Pending
172 })
173 .await
174}