agnostic_lite/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![forbid(unsafe_code)]
4#![deny(warnings, missing_docs)]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![cfg_attr(docsrs, allow(unused_attributes))]
7
8#[cfg(all(feature = "alloc", not(feature = "std")))]
9extern crate alloc as std;
10
11#[cfg(feature = "std")]
12extern crate std;
13
14macro_rules! cfg_time_with_docsrs {
15  ($($item:item)*) => {
16    $(
17      #[cfg(feature = "time")]
18      #[cfg_attr(docsrs, doc(cfg(feature = "time")))]
19      $item
20    )*
21  };
22}
23
24macro_rules! cfg_time {
25  ($($item:item)*) => {
26    $(
27      #[cfg(feature = "time")]
28      $item
29    )*
30  };
31}
32
33use core::future::Future;
34
35cfg_time_with_docsrs!(
36  /// Time related traits
37  pub mod time;
38);
39
40/// Macro to conditionally compile items for `tokio` feature
41#[macro_export]
42macro_rules! cfg_tokio {
43  ($($item:item)*) => {
44    $(
45      #[cfg(feature = "tokio")]
46      #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
47      $item
48    )*
49  };
50  (@no_doc_cfg $($item:item)*) => {
51    $(
52      #[cfg(feature = "tokio")]
53      $item
54    )*
55  };
56}
57
58/// Macro to conditionally compile items for `smol` feature
59#[macro_export]
60macro_rules! cfg_smol {
61  ($($item:item)*) => {
62    $(
63      #[cfg(feature = "smol")]
64      #[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
65      $item
66    )*
67  };
68  (@no_doc_cfg $($item:item)*) => {
69    $(
70      #[cfg(feature = "smol")]
71      $item
72    )*
73  };
74}
75
76/// Macro to conditionally compile items for `unix` system
77#[macro_export]
78macro_rules! cfg_unix {
79  ($($item:item)*) => {
80    $(
81      #[cfg(feature = "unix")]
82      #[cfg_attr(docsrs, doc(cfg(feature = "unix")))]
83      $item
84    )*
85  };
86  (@no_doc_cfg $($item:item)*) => {
87    $(
88      #[cfg(feature = "unix")]
89      $item
90    )*
91  };
92}
93
94/// Macro to conditionally compile items for `windows` system
95#[macro_export]
96macro_rules! cfg_windows {
97  ($($item:item)*) => {
98    $(
99      #[cfg(feature = "windows")]
100      #[cfg_attr(docsrs, doc(cfg(feature = "windows")))]
101      $item
102    )*
103  };
104  (@no_doc_cfg $($item:item)*) => {
105    $(
106      #[cfg(feature = "windows")]
107      $item
108    )*
109  };
110}
111
112/// Macro to conditionally compile items for `linux` system
113#[macro_export]
114macro_rules! cfg_linux {
115  ($($item:item)*) => {
116    $(
117      #[cfg(target_os = "linux")]
118      #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
119      $item
120    )*
121  };
122  (@no_doc_cfg $($item:item)*) => {
123    $(
124      #[cfg(target_os = "linux")]
125      $item
126    )*
127  };
128}
129
130#[macro_use]
131mod spawner;
132
133/// Concrete runtime implementations based on [`tokio`] runtime.
134///
135/// [`tokio`]: https://docs.rs/tokio
136#[cfg(feature = "tokio")]
137#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
138pub mod tokio;
139
140/// Concrete runtime implementations based on [`smol`] runtime.
141///
142/// [`smol`]: https://docs.rs/smol
143#[cfg(feature = "smol")]
144#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
145pub mod smol;
146
147/// Concrete runtime implementations based on [`wasm-bindgen-futures`].
148///
149/// [`wasm-bindgen-futures`]: https://docs.rs/wasm-bindgen-futures
150#[cfg(feature = "wasm")]
151#[cfg_attr(docsrs, doc(cfg(feature = "wasm")))]
152pub mod wasm;
153
154/// Time related traits concrete implementations for runtime based on [`async-io`](::async_io), e.g. [`smol`].
155///
156/// [`smol`]: https://docs.rs/smol
157#[cfg(feature = "async-io")]
158#[cfg_attr(docsrs, doc(cfg(feature = "async-io")))]
159pub mod async_io;
160
161pub use spawner::*;
162
163/// Yielder hints the runtime to execution back
164pub trait Yielder {
165  /// Yields execution back to the runtime.
166  fn yield_now() -> impl Future<Output = ()> + Send;
167
168  /// Yields execution back to the runtime.
169  fn yield_now_local() -> impl Future<Output = ()>;
170}
171
172/// Runtime trait
173pub trait RuntimeLite: Sized + Unpin + Copy + Send + Sync + 'static {
174  /// The spawner type for this runtime
175  type Spawner: AsyncSpawner;
176  /// The local spawner type for this runtime
177  type LocalSpawner: AsyncLocalSpawner;
178  /// The blocking spawner type for this runtime
179  type BlockingSpawner: AsyncBlockingSpawner;
180
181  cfg_time_with_docsrs!(
182    /// The instant type for this runtime
183    type Instant: time::Instant;
184
185    /// The after spawner type for this runtime
186    type AfterSpawner: AsyncAfterSpawner<Instant = Self::Instant>;
187
188    /// The interval type for this runtime
189    type Interval: time::AsyncInterval<Instant = Self::Instant>;
190
191    /// The local interval type for this runtime
192    type LocalInterval: time::AsyncLocalInterval<Instant = Self::Instant>;
193
194    /// The sleep type for this runtime
195    type Sleep: time::AsyncSleep<Instant = Self::Instant>;
196
197    /// The local sleep type for this runtime
198    type LocalSleep: time::AsyncLocalSleep<Instant = Self::Instant>;
199
200    /// The delay type for this runtime
201    type Delay<F>: time::AsyncDelay<F, Instant = Self::Instant>
202    where
203      F: Future + Send;
204
205    /// The local delay type for this runtime
206    type LocalDelay<F>: time::AsyncLocalDelay<F, Instant = Self::Instant>
207    where
208      F: Future;
209
210    /// The timeout type for this runtime
211    type Timeout<F>: time::AsyncTimeout<F, Instant = Self::Instant>
212    where
213      F: Future + Send;
214
215    /// The local timeout type for this runtime
216    type LocalTimeout<F>: time::AsyncLocalTimeout<F, Instant = Self::Instant>
217    where
218      F: Future;
219  );
220
221  /// Create a new instance of the runtime
222  fn new() -> Self;
223
224  /// Returns the name of the runtime
225  ///
226  /// See also fully qualified name of the runtime
227  fn name() -> &'static str;
228
229  /// Returns the fully qualified name of the runtime
230  ///
231  /// See also [`name`](RuntimeLite::name) of the runtime
232  fn fqname() -> &'static str;
233
234  /// Spawn a future onto the runtime
235  fn spawn<F>(future: F) -> <Self::Spawner as AsyncSpawner>::JoinHandle<F::Output>
236  where
237    F::Output: Send + 'static,
238    F: Future + Send + 'static,
239  {
240    <Self::Spawner as AsyncSpawner>::spawn(future)
241  }
242
243  /// Spawn a future onto the runtime and detach it
244  fn spawn_detach<F>(future: F)
245  where
246    F::Output: Send + 'static,
247    F: Future + Send + 'static,
248  {
249    <Self::Spawner as AsyncSpawner>::spawn_detach(future);
250  }
251
252  /// Spawn a future onto the local runtime
253  fn spawn_local<F>(future: F) -> <Self::LocalSpawner as AsyncLocalSpawner>::JoinHandle<F::Output>
254  where
255    F: Future + 'static,
256    F::Output: 'static,
257  {
258    <Self::LocalSpawner as AsyncLocalSpawner>::spawn_local(future)
259  }
260
261  /// Spawn a future onto the local runtime and detach it
262  fn spawn_local_detach<F>(future: F)
263  where
264    F: Future + 'static,
265    F::Output: 'static,
266  {
267    <Self::LocalSpawner as AsyncLocalSpawner>::spawn_local_detach(future)
268  }
269
270  /// Spawn a blocking function onto the runtime
271  fn spawn_blocking<F, R>(f: F) -> <Self::BlockingSpawner as AsyncBlockingSpawner>::JoinHandle<R>
272  where
273    F: FnOnce() -> R + Send + 'static,
274    R: Send + 'static,
275  {
276    <Self::BlockingSpawner as AsyncBlockingSpawner>::spawn_blocking(f)
277  }
278
279  /// Spawn a blocking function onto the runtime and detach it
280  fn spawn_blocking_detach<F, R>(f: F)
281  where
282    F: FnOnce() -> R + Send + 'static,
283    R: Send + 'static,
284  {
285    <Self::BlockingSpawner as AsyncBlockingSpawner>::spawn_blocking_detach(f);
286  }
287
288  /// Block the current thread on the given future
289  fn block_on<F: Future>(f: F) -> F::Output;
290
291  /// Yield the current task
292  fn yield_now() -> impl Future<Output = ()> + Send;
293
294  cfg_time_with_docsrs!(
295    /// Returns an instant corresponding to "now".
296    fn now() -> Self::Instant {
297      <Self::Instant as time::Instant>::now()
298    }
299
300    /// Spawn a future onto the runtime and run the given future after the given duration
301    fn spawn_after<F>(
302      duration: core::time::Duration,
303      future: F,
304    ) -> <Self::AfterSpawner as AsyncAfterSpawner>::JoinHandle<F::Output>
305    where
306      F::Output: Send + 'static,
307      F: Future + Send + 'static,
308    {
309      <Self::AfterSpawner as AsyncAfterSpawner>::spawn_after(duration, future)
310    }
311
312    /// Spawn a future onto the runtime and run the given future after the given instant.
313    fn spawn_after_at<F>(
314      at: Self::Instant,
315      future: F,
316    ) -> <Self::AfterSpawner as AsyncAfterSpawner>::JoinHandle<F::Output>
317    where
318      F::Output: Send + 'static,
319      F: Future + Send + 'static,
320    {
321      <Self::AfterSpawner as AsyncAfterSpawner>::spawn_after_at(at, future)
322    }
323
324    /// Create a new interval that starts at the current time and
325    /// yields every `period` duration
326    fn interval(interval: core::time::Duration) -> Self::Interval;
327
328    /// Create a new interval that starts at the given instant and
329    /// yields every `period` duration
330    fn interval_at(start: Self::Instant, period: core::time::Duration) -> Self::Interval;
331
332    /// Create a new interval that starts at the current time and
333    /// yields every `period` duration
334    fn interval_local(interval: core::time::Duration) -> Self::LocalInterval;
335
336    /// Create a new interval that starts at the given instant and
337    /// yields every `period` duration
338    fn interval_local_at(start: Self::Instant, period: core::time::Duration)
339    -> Self::LocalInterval;
340
341    /// Create a new sleep future that completes after the given duration
342    /// has elapsed
343    fn sleep(duration: core::time::Duration) -> Self::Sleep;
344
345    /// Create a new sleep future that completes at the given instant
346    /// has elapsed
347    fn sleep_until(instant: Self::Instant) -> Self::Sleep;
348
349    /// Create a new sleep future that completes after the given duration
350    /// has elapsed
351    fn sleep_local(duration: core::time::Duration) -> Self::LocalSleep;
352
353    /// Create a new sleep future that completes at the given instant
354    /// has elapsed
355    fn sleep_local_until(instant: Self::Instant) -> Self::LocalSleep;
356
357    /// Create a new delay future that runs the `fut` after the given duration
358    /// has elapsed. The `Future` will never be polled until the duration has
359    /// elapsed.
360    ///
361    /// The behavior of this function may different in different runtime implementations.
362    fn delay<F>(duration: core::time::Duration, fut: F) -> Self::Delay<F>
363    where
364      F: Future + Send;
365
366    /// Like [`delay`](RuntimeLite::delay), but does not require the `fut` to be `Send`.
367    /// Create a new delay future that runs the `fut` after the given duration
368    /// has elapsed. The `Future` will never be polled until the duration has
369    /// elapsed.
370    ///
371    /// The behavior of this function may different in different runtime implementations.
372    fn delay_local<F>(duration: core::time::Duration, fut: F) -> Self::LocalDelay<F>
373    where
374      F: Future;
375
376    /// Create a new timeout future that runs the `future` after the given deadline.
377    /// The `Future` will never be polled until the deadline has reached.
378    ///
379    /// The behavior of this function may different in different runtime implementations.
380    fn delay_at<F>(deadline: Self::Instant, fut: F) -> Self::Delay<F>
381    where
382      F: Future + Send;
383
384    /// Like [`delay_at`](RuntimeLite::delay_at), but does not require the `fut` to be `Send`.
385    /// Create a new timeout future that runs the `future` after the given deadline
386    /// The `Future` will never be polled until the deadline has reached.
387    ///
388    /// The behavior of this function may different in different runtime implementations.
389    fn delay_local_at<F>(deadline: Self::Instant, fut: F) -> Self::LocalDelay<F>
390    where
391      F: Future;
392
393    /// Requires a `Future` to complete before the specified duration has elapsed.
394    ///
395    /// The behavior of this function may different in different runtime implementations.
396    fn timeout<F>(duration: core::time::Duration, future: F) -> Self::Timeout<F>
397    where
398      F: Future + Send;
399
400    /// Requires a `Future` to complete before the specified instant in time.
401    ///
402    /// The behavior of this function may different in different runtime implementations.
403    fn timeout_at<F>(deadline: Self::Instant, future: F) -> Self::Timeout<F>
404    where
405      F: Future + Send;
406
407    /// Like [`timeout`](RuntimeLite::timeout), but does not requrie the `future` to be `Send`.
408    /// Requires a `Future` to complete before the specified duration has elapsed.
409    ///
410    /// The behavior of this function may different in different runtime implementations.
411    fn timeout_local<F>(duration: core::time::Duration, future: F) -> Self::LocalTimeout<F>
412    where
413      F: Future;
414
415    /// Like [`timeout_at`](RuntimeLite::timeout_at), but does not requrie the `future` to be `Send`.
416    /// Requires a `Future` to complete before the specified duration has elapsed.
417    ///
418    /// The behavior of this function may different in different runtime implementations.
419    fn timeout_local_at<F>(deadline: Self::Instant, future: F) -> Self::LocalTimeout<F>
420    where
421      F: Future;
422  );
423}
424
425/// Unit test for the [`RuntimeLite`]
426#[cfg(all(any(test, feature = "test"), feature = "std"))]
427#[cfg_attr(docsrs, doc(cfg(any(test, feature = "test"))))]
428pub mod tests {
429  use core::sync::atomic::{AtomicUsize, Ordering};
430
431  use std::{sync::Arc, time::Duration};
432
433  use super::{AfterHandle, RuntimeLite};
434
435  /// Unit test for the [`RuntimeLite::spawn_after`] function
436  pub async fn spawn_after_unittest<R: RuntimeLite>() {
437    let ctr = Arc::new(AtomicUsize::new(1));
438    let ctr1 = ctr.clone();
439    let handle = R::spawn_after(Duration::from_secs(1), async move {
440      ctr1.fetch_add(1, Ordering::SeqCst);
441    });
442
443    R::sleep(Duration::from_millis(500)).await;
444    assert_eq!(ctr.load(Ordering::SeqCst), 1);
445
446    handle.await.unwrap();
447    assert_eq!(ctr.load(Ordering::SeqCst), 2);
448  }
449
450  /// Unit test for the [`RuntimeLite::spawn_after`] function
451  ///
452  /// The task will be canceled before it completes
453  pub async fn spawn_after_cancel_unittest<R: RuntimeLite>() {
454    let ctr = Arc::new(AtomicUsize::new(1));
455    let ctr1 = ctr.clone();
456    let handle = R::spawn_after(Duration::from_secs(1), async move {
457      ctr1.fetch_add(1, Ordering::SeqCst);
458    });
459
460    R::sleep(Duration::from_millis(500)).await;
461    assert_eq!(ctr.load(Ordering::SeqCst), 1);
462
463    let o = handle.cancel().await;
464    assert!(o.is_none());
465    assert_eq!(ctr.load(Ordering::SeqCst), 1);
466  }
467
468  /// Unit test for the [`RuntimeLite::spawn_after`] function
469  ///
470  /// The [`AfterHandle`] will be dropped immediately after it is created
471  pub async fn spawn_after_drop_unittest<R: RuntimeLite>() {
472    let ctr = Arc::new(AtomicUsize::new(1));
473    let ctr1 = ctr.clone();
474    drop(R::spawn_after(Duration::from_secs(1), async move {
475      ctr1.fetch_add(1, Ordering::SeqCst);
476    }));
477
478    R::sleep(Duration::from_millis(500)).await;
479    assert_eq!(ctr.load(Ordering::SeqCst), 1);
480
481    R::sleep(Duration::from_millis(600)).await;
482    assert_eq!(ctr.load(Ordering::SeqCst), 2);
483  }
484
485  /// Unit test for the [`RuntimeLite::spawn_after`] function
486  ///
487  /// The [`AfterHandle`] will be abort after it is created, and the task will not be executed.
488  pub async fn spawn_after_abort_unittest<R: RuntimeLite>() {
489    let ctr = Arc::new(AtomicUsize::new(1));
490    let ctr1 = ctr.clone();
491    let handle = R::spawn_after(Duration::from_secs(1), async move {
492      ctr1.fetch_add(1, Ordering::SeqCst);
493    });
494
495    R::sleep(Duration::from_millis(500)).await;
496    assert_eq!(ctr.load(Ordering::SeqCst), 1);
497
498    handle.abort();
499    R::sleep(Duration::from_millis(600)).await;
500    assert_eq!(ctr.load(Ordering::SeqCst), 1);
501  }
502
503  /// Unit test for the [`RuntimeLite::spawn_after`] function
504  ///
505  /// The [`AfterHandle`] will be reset to passed than the original duration after it is created, and the task will be executed after the reset duration.
506  pub async fn spawn_after_reset_to_pass_unittest<R: RuntimeLite>() {
507    let ctr = Arc::new(AtomicUsize::new(1));
508    let ctr1 = ctr.clone();
509    let handle = R::spawn_after(Duration::from_secs(1), async move {
510      ctr1.fetch_add(1, Ordering::SeqCst);
511    });
512
513    R::sleep(Duration::from_millis(500)).await;
514    assert_eq!(ctr.load(Ordering::SeqCst), 1);
515
516    handle.reset(Duration::from_millis(250));
517    R::sleep(Duration::from_millis(10)).await;
518    assert_eq!(ctr.load(Ordering::SeqCst), 2);
519  }
520
521  /// Unit test for the [`RuntimeLite::spawn_after`] function
522  ///
523  /// The [`AfterHandle`] will be reset to future than the original duration after it is created, and the task will be executed after the reset duration.
524  pub async fn spawn_after_reset_to_future_unittest<R: RuntimeLite>() {
525    let ctr = Arc::new(AtomicUsize::new(1));
526    let ctr1 = ctr.clone();
527    let handle = R::spawn_after(Duration::from_secs(1), async move {
528      ctr1.fetch_add(1, Ordering::SeqCst);
529    });
530
531    R::sleep(Duration::from_millis(500)).await;
532    assert_eq!(ctr.load(Ordering::SeqCst), 1);
533
534    handle.reset(Duration::from_millis(1250)); // now delay 1.25s
535    R::sleep(Duration::from_millis(750 + 10)).await; // we already delayed 500ms, so remaining is 750ms
536    assert_eq!(ctr.load(Ordering::SeqCst), 2);
537  }
538}