1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![deny(
4 nonstandard_style,
5 rust_2018_idioms,
6 rustdoc::broken_intra_doc_links,
7 rustdoc::private_intra_doc_links
8)]
9#![forbid(non_ascii_idents, unsafe_code)]
10#![warn(
11 deprecated_in_future,
12 missing_copy_implementations,
13 missing_debug_implementations,
14 missing_docs,
15 unreachable_pub,
16 unused_import_braces,
17 unused_labels,
18 unused_lifetimes,
19 unused_qualifications,
20 unused_results
21)]
22#![allow(clippy::uninlined_format_args)]
23
24use std::{any::Any, fmt, future::Future, time::Duration};
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28#[non_exhaustive]
29pub enum Runtime {
30 #[cfg(feature = "tokio_1")]
31 #[cfg_attr(docsrs, doc(cfg(feature = "tokio_1")))]
32 Tokio1,
34
35 #[cfg(feature = "async-std_1")]
36 #[cfg_attr(docsrs, doc(cfg(feature = "async-std_1")))]
37 #[deprecated(
38 note = "Support for `async-std` is deprecated and will be removed in a future version. Consider using `tokio_1` or `smol_2` instead."
39 )]
40 AsyncStd1,
42
43 #[cfg(feature = "smol_2")]
44 #[cfg_attr(docsrs, doc(cfg(feature = "smol_2")))]
45 Smol2,
47}
48
49#[allow(unused_variables)]
56pub async fn timeout<F>(runtime: Runtime, duration: Duration, future: F) -> Option<F::Output>
57where
58 F: Future,
59{
60 match runtime {
61 #[cfg(feature = "tokio_1")]
62 Runtime::Tokio1 => tokio_1::time::timeout(duration, future).await.ok(),
63 #[cfg(feature = "async-std_1")]
64 #[allow(deprecated)]
65 Runtime::AsyncStd1 => async_std_1::future::timeout(duration, future).await.ok(),
66 #[cfg(feature = "smol_2")]
67 Runtime::Smol2 => {
68 smol_2_futures_lite::future::or(async { Some(future.await) }, async {
69 let _ = smol_2_async_io::Timer::after(duration).await;
70 None
71 })
72 .await
73 }
74 #[allow(unreachable_patterns)]
75 _ => unreachable!(),
76 }
77}
78
79#[allow(unused_variables)]
85pub async fn spawn_blocking<F, R>(runtime: Runtime, f: F) -> Result<R, SpawnBlockingError>
86where
87 F: FnOnce() -> R + Send + 'static,
88 R: Send + 'static,
89{
90 match runtime {
91 #[cfg(feature = "tokio_1")]
92 Runtime::Tokio1 => tokio_1::task::spawn_blocking(f).await.map_err(|e| {
93 if e.is_cancelled() {
94 SpawnBlockingError::Cancelled
95 } else {
96 SpawnBlockingError::Panic(e.into_panic())
97 }
98 }),
99 #[cfg(feature = "async-std_1")]
100 #[allow(deprecated)]
101 Runtime::AsyncStd1 => Ok(async_std_1::task::spawn_blocking(f).await),
102 #[cfg(feature = "smol_2")]
103 Runtime::Smol2 => Ok(smol_2_blocking::unblock(f).await),
104 #[allow(unreachable_patterns)]
105 _ => unreachable!(),
106 }
107}
108
109#[allow(unused_variables)]
118pub fn spawn_blocking_background<F>(runtime: Runtime, f: F) -> Result<(), SpawnBlockingError>
119where
120 F: FnOnce() + Send + 'static,
121{
122 match runtime {
123 #[cfg(feature = "tokio_1")]
124 Runtime::Tokio1 => {
125 drop(tokio_1::task::spawn_blocking(f));
126 Ok(())
127 }
128 #[cfg(feature = "async-std_1")]
129 #[allow(deprecated)]
130 Runtime::AsyncStd1 => {
131 drop(async_std_1::task::spawn_blocking(f));
132 Ok(())
133 }
134 #[cfg(feature = "smol_2")]
135 Runtime::Smol2 => {
136 drop(smol_2_blocking::unblock(f));
137 Ok(())
138 }
139 #[allow(unreachable_patterns)]
140 _ => unreachable!(),
141 }
142}
143
144#[derive(Debug)]
146pub enum SpawnBlockingError {
147 Panic(Box<dyn Any + Send + 'static>),
149
150 Cancelled,
152}
153
154impl fmt::Display for SpawnBlockingError {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 Self::Panic(p) => write!(f, "SpawnBlockingError: Panic: {:?}", p),
158 Self::Cancelled => write!(f, "SpawnBlockingError: Cancelled"),
159 }
160 }
161}
162
163impl std::error::Error for SpawnBlockingError {}
164
165#[cfg(all(test, feature = "tokio_1"))]
166mod tests_with_tokio_1 {
167 use super::*;
168
169 #[tokio_1::test(crate = "tokio_1")]
170 async fn test_spawning_blocking() {
171 assert!(spawn_blocking(Runtime::Tokio1, || 42).await.is_ok());
172 }
173
174 #[tokio_1::test(crate = "tokio_1")]
175 async fn test_spawning_blocking_can_panic() {
176 assert!(matches!(
177 spawn_blocking(Runtime::Tokio1, || {
178 panic!("42");
179 })
180 .await,
181 Err(SpawnBlockingError::Panic(_))
182 ));
183 }
184}