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)]
28pub enum Runtime {
29 #[cfg(feature = "tokio_1")]
30 #[cfg_attr(docsrs, doc(cfg(feature = "tokio_1")))]
31 Tokio1,
33
34 #[cfg(feature = "async-std_1")]
35 #[cfg_attr(docsrs, doc(cfg(feature = "async-std_1")))]
36 #[deprecated(
37 note = "Support for `async-std` is deprecated and will be removed in a future version. Consider using `tokio_1` or `smol_2` instead."
38 )]
39 AsyncStd1,
41
42 #[cfg(feature = "smol_2")]
43 #[cfg_attr(docsrs, doc(cfg(feature = "smol_2")))]
44 Smol2,
46}
47
48impl Runtime {
49 #[allow(unused_variables)]
56 pub async fn timeout<F>(&self, duration: Duration, future: F) -> Option<F::Output>
57 where
58 F: Future,
59 {
60 match self {
61 #[cfg(feature = "tokio_1")]
62 Self::Tokio1 => tokio_1::time::timeout(duration, future).await.ok(),
63 #[cfg(feature = "async-std_1")]
64 #[allow(deprecated)]
65 Self::AsyncStd1 => async_std_1::future::timeout(duration, future).await.ok(),
66 #[cfg(feature = "smol_2")]
67 Self::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)]
85 pub async fn spawn_blocking<F, R>(&self, f: F) -> Result<R, SpawnBlockingError>
86 where
87 F: FnOnce() -> R + Send + 'static,
88 R: Send + 'static,
89 {
90 match self {
91 #[cfg(feature = "tokio_1")]
92 Self::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 Self::AsyncStd1 => Ok(async_std_1::task::spawn_blocking(f).await),
102 #[cfg(feature = "smol_2")]
103 Self::Smol2 => Ok(smol_2_blocking::unblock(f).await),
104 #[allow(unreachable_patterns)]
105 _ => unreachable!(),
106 }
107 }
108
109 #[allow(unused_variables)]
118 pub fn spawn_blocking_background<F>(&self, f: F) -> Result<(), SpawnBlockingError>
119 where
120 F: FnOnce() + Send + 'static,
121 {
122 match self {
123 #[cfg(feature = "tokio_1")]
124 Self::Tokio1 => {
125 drop(tokio_1::task::spawn_blocking(f));
126 Ok(())
127 }
128 #[cfg(feature = "async-std_1")]
129 #[allow(deprecated)]
130 Self::AsyncStd1 => {
131 drop(async_std_1::task::spawn_blocking(f));
132 Ok(())
133 }
134 #[cfg(feature = "smol_2")]
135 Self::Smol2 => {
136 drop(smol_2_blocking::unblock(f));
137 Ok(())
138 }
139 #[allow(unreachable_patterns)]
140 _ => unreachable!(),
141 }
142 }
143}
144
145#[derive(Debug)]
147pub enum SpawnBlockingError {
148 Panic(Box<dyn Any + Send + 'static>),
150
151 Cancelled,
153}
154
155impl fmt::Display for SpawnBlockingError {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 Self::Panic(p) => write!(f, "SpawnBlockingError: Panic: {:?}", p),
159 Self::Cancelled => write!(f, "SpawnBlockingError: Cancelled"),
160 }
161 }
162}
163
164impl std::error::Error for SpawnBlockingError {}
165
166#[cfg(all(test, feature = "tokio_1"))]
167mod tests_with_tokio_1 {
168 use super::{Runtime, SpawnBlockingError};
169
170 #[tokio_1::test(crate = "tokio_1")]
171 async fn test_spawning_blocking() {
172 assert!(Runtime::Tokio1.spawn_blocking(|| 42).await.is_ok());
173 }
174
175 #[tokio_1::test(crate = "tokio_1")]
176 async fn test_spawning_blocking_can_panic() {
177 assert!(matches!(
178 Runtime::Tokio1
179 .spawn_blocking(|| {
180 panic!("42");
181 })
182 .await,
183 Err(SpawnBlockingError::Panic(_))
184 ));
185 }
186}