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