tor_rtcompat/
tokio.rs

1//! Entry points for use with Tokio runtimes.
2use crate::impls::tokio::TokioRuntimeHandle as Handle;
3
4use crate::{CompoundRuntime, RealCoarseTimeProvider, ToplevelBlockOn};
5use std::io::{Error as IoError, ErrorKind, Result as IoResult};
6
7#[cfg(feature = "native-tls")]
8use crate::impls::native_tls::NativeTlsProvider;
9#[cfg(feature = "rustls")]
10use crate::impls::rustls::RustlsProvider;
11
12/// An alias for the Tokio runtime that we prefer to use, based on whatever TLS
13/// implementation has been enabled.
14///
15/// If only one of `native_tls` and `rustls` bas been enabled within the
16/// `tor-rtcompat` crate, that will be the TLS backend that this uses.
17///
18/// Currently, `native_tls` is preferred over `rustls` when both are available,
19/// because of its maturity within Arti.  However, this might change in the
20/// future.
21#[cfg(feature = "native-tls")]
22pub use TokioNativeTlsRuntime as PreferredRuntime;
23#[cfg(all(feature = "rustls", not(feature = "native-tls")))]
24pub use TokioRustlsRuntime as PreferredRuntime;
25
26/// A [`Runtime`](crate::Runtime) built around a Handle to a tokio runtime, and `native_tls`.
27///
28/// # Limitations
29///
30/// Note that Arti requires that the runtime should have working
31/// implementations for Tokio's time, net, and io facilities, but we have
32/// no good way to check that when creating this object.
33#[derive(Clone)]
34#[cfg(feature = "native-tls")]
35pub struct TokioNativeTlsRuntime {
36    /// The actual [`CompoundRuntime`] that implements this.
37    inner: HandleInner,
38}
39
40/// Implementation type for a TokioRuntimeHandle.
41#[cfg(feature = "native-tls")]
42type HandleInner = CompoundRuntime<
43    Handle,
44    Handle,
45    RealCoarseTimeProvider,
46    Handle,
47    Handle,
48    NativeTlsProvider,
49    Handle,
50>;
51
52/// A [`Runtime`](crate::Runtime) built around a Handle to a tokio runtime, and `rustls`.
53#[derive(Clone)]
54#[cfg(feature = "rustls")]
55pub struct TokioRustlsRuntime {
56    /// The actual [`CompoundRuntime`] that implements this.
57    inner: RustlsHandleInner,
58}
59
60/// Implementation for a TokioRuntimeRustlsHandle
61#[cfg(feature = "rustls")]
62type RustlsHandleInner =
63    CompoundRuntime<Handle, Handle, RealCoarseTimeProvider, Handle, Handle, RustlsProvider, Handle>;
64
65#[cfg(feature = "native-tls")]
66crate::opaque::implement_opaque_runtime! {
67    TokioNativeTlsRuntime { inner : HandleInner }
68}
69
70#[cfg(feature = "rustls")]
71crate::opaque::implement_opaque_runtime! {
72    TokioRustlsRuntime { inner : RustlsHandleInner }
73}
74
75#[cfg(feature = "native-tls")]
76impl From<tokio_crate::runtime::Handle> for TokioNativeTlsRuntime {
77    fn from(h: tokio_crate::runtime::Handle) -> Self {
78        let h = Handle::new(h);
79        TokioNativeTlsRuntime {
80            inner: CompoundRuntime::new(
81                h.clone(),
82                h.clone(),
83                RealCoarseTimeProvider::new(),
84                h.clone(),
85                h.clone(),
86                NativeTlsProvider::default(),
87                h,
88            ),
89        }
90    }
91}
92
93#[cfg(feature = "rustls")]
94impl From<tokio_crate::runtime::Handle> for TokioRustlsRuntime {
95    fn from(h: tokio_crate::runtime::Handle) -> Self {
96        let h = Handle::new(h);
97        TokioRustlsRuntime {
98            inner: CompoundRuntime::new(
99                h.clone(),
100                h.clone(),
101                RealCoarseTimeProvider::new(),
102                h.clone(),
103                h.clone(),
104                RustlsProvider::default(),
105                h,
106            ),
107        }
108    }
109}
110
111#[cfg(feature = "native-tls")]
112impl TokioNativeTlsRuntime {
113    /// Create a new [`TokioNativeTlsRuntime`].
114    ///
115    /// The return value will own the underlying Tokio runtime object, which
116    /// will be dropped when the last copy of this handle is freed.
117    ///
118    /// If you want to use a currently running runtime instead, call
119    /// [`TokioNativeTlsRuntime::current()`].
120    pub fn create() -> IoResult<Self> {
121        crate::impls::tokio::create_runtime().map(|r| TokioNativeTlsRuntime {
122            inner: CompoundRuntime::new(
123                r.clone(),
124                r.clone(),
125                RealCoarseTimeProvider::new(),
126                r.clone(),
127                r.clone(),
128                NativeTlsProvider::default(),
129                r,
130            ),
131        })
132    }
133
134    /// Return a [`TokioNativeTlsRuntime`] wrapping the currently running
135    /// Tokio runtime.
136    ///
137    /// # Usage note
138    ///
139    /// We should never call this from inside other Arti crates, or from library
140    /// crates that want to support multiple runtimes!  This function is for
141    /// Arti _users_ who want to wrap some existing Tokio runtime as a
142    /// [`Runtime`](crate::Runtime).  It is not for library crates that want to work with
143    /// multiple runtimes.
144    ///
145    /// Once you have a runtime returned by this function, you should just
146    /// create more handles to it via [`Clone`].
147    pub fn current() -> IoResult<Self> {
148        Ok(current_handle()?.into())
149    }
150
151    /// Helper to run a single test function in a freshly created runtime.
152    ///
153    /// # Panics
154    ///
155    /// Panics if we can't create this runtime.
156    ///
157    /// # Warning
158    ///
159    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
160    #[doc(hidden)]
161    pub fn run_test<P, F, O>(func: P) -> O
162    where
163        P: FnOnce(Self) -> F,
164        F: futures::Future<Output = O>,
165    {
166        let runtime = Self::create().expect("Failed to create runtime");
167        runtime.clone().block_on(func(runtime))
168    }
169}
170
171#[cfg(feature = "rustls")]
172impl TokioRustlsRuntime {
173    /// Create a new [`TokioRustlsRuntime`].
174    ///
175    /// The return value will own the underlying Tokio runtime object, which
176    /// will be dropped when the last copy of this handle is freed.
177    ///
178    /// If you want to use a currently running runtime instead, call
179    /// [`TokioRustlsRuntime::current()`].
180    pub fn create() -> IoResult<Self> {
181        crate::impls::tokio::create_runtime().map(|r| TokioRustlsRuntime {
182            inner: CompoundRuntime::new(
183                r.clone(),
184                r.clone(),
185                RealCoarseTimeProvider::new(),
186                r.clone(),
187                r.clone(),
188                RustlsProvider::default(),
189                r,
190            ),
191        })
192    }
193
194    /// Return a [`TokioRustlsRuntime`] wrapping the currently running
195    /// Tokio runtime.
196    ///
197    /// # Usage note
198    ///
199    /// We should never call this from inside other Arti crates, or from library
200    /// crates that want to support multiple runtimes!  This function is for
201    /// Arti _users_ who want to wrap some existing Tokio runtime as a
202    /// [`Runtime`](crate::Runtime).  It is not for library crates that want to work with
203    /// multiple runtimes.
204    ///
205    /// Once you have a runtime returned by this function, you should just
206    /// create more handles to it via [`Clone`].
207    pub fn current() -> IoResult<Self> {
208        Ok(current_handle()?.into())
209    }
210
211    /// Helper to run a single test function in a freshly created runtime.
212    ///
213    /// # Panics
214    ///
215    /// Panics if we can't create this runtime.
216    ///
217    /// # Warning
218    ///
219    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
220    #[doc(hidden)]
221    pub fn run_test<P, F, O>(func: P) -> O
222    where
223        P: FnOnce(Self) -> F,
224        F: futures::Future<Output = O>,
225    {
226        let runtime = Self::create().expect("Failed to create runtime");
227        runtime.clone().block_on(func(runtime))
228    }
229}
230
231/// As `Handle::try_current()`, but return an IoError on failure.
232#[cfg(any(feature = "native-tls", feature = "rustls"))]
233fn current_handle() -> std::io::Result<tokio_crate::runtime::Handle> {
234    tokio_crate::runtime::Handle::try_current().map_err(|e| IoError::new(ErrorKind::Other, e))
235}
236
237#[cfg(all(
238    test,
239    not(miri), // tokio makes some syscalls that don't work with miri
240))]
241mod test {
242    // @@ begin test lint list maintained by maint/add_warning @@
243    #![allow(clippy::bool_assert_comparison)]
244    #![allow(clippy::clone_on_copy)]
245    #![allow(clippy::dbg_macro)]
246    #![allow(clippy::mixed_attributes_style)]
247    #![allow(clippy::print_stderr)]
248    #![allow(clippy::print_stdout)]
249    #![allow(clippy::single_char_pattern)]
250    #![allow(clippy::unwrap_used)]
251    #![allow(clippy::unchecked_duration_subtraction)]
252    #![allow(clippy::useless_vec)]
253    #![allow(clippy::needless_pass_by_value)]
254    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
255    use super::*;
256
257    #[test]
258    fn no_current() {
259        // There should be no running tokio runtime in this context.
260
261        #[cfg(feature = "native-tls")]
262        assert!(TokioNativeTlsRuntime::current().is_err());
263
264        #[cfg(feature = "rustls")]
265        assert!(TokioRustlsRuntime::current().is_err());
266    }
267
268    #[test]
269    fn current() {
270        // Now start a tokio runtime and make sure that the "current" functions do work in that case.
271        let runtime = PreferredRuntime::create().unwrap();
272        runtime.block_on(async {
273            #[cfg(feature = "native-tls")]
274            assert!(TokioNativeTlsRuntime::current().is_ok());
275
276            #[cfg(feature = "rustls")]
277            assert!(TokioRustlsRuntime::current().is_ok());
278        });
279    }
280
281    #[test]
282    fn debug() {
283        #[cfg(feature = "native-tls")]
284        assert_eq!(
285            format!("{:?}", TokioNativeTlsRuntime::create().unwrap()),
286            "TokioNativeTlsRuntime { .. }"
287        );
288        #[cfg(feature = "rustls")]
289        assert_eq!(
290            format!("{:?}", TokioRustlsRuntime::create().unwrap()),
291            "TokioRustlsRuntime { .. }"
292        );
293
294        // Just for fun, let's try the Debug output for the Compound.
295        assert_eq!(
296            format!("{:?}", PreferredRuntime::create().unwrap().inner),
297            "CompoundRuntime { .. }"
298        );
299    }
300}