actix_rt/
runtime.rs

1use std::{future::Future, io};
2
3use tokio::task::{JoinHandle, LocalSet};
4
5/// A Tokio-based runtime proxy.
6///
7/// All spawned futures will be executed on the current thread. Therefore, there is no `Send` bound
8/// on submitted futures.
9#[derive(Debug)]
10pub struct Runtime {
11    local: LocalSet,
12    rt: tokio::runtime::Runtime,
13}
14
15pub(crate) fn default_tokio_runtime() -> io::Result<tokio::runtime::Runtime> {
16    tokio::runtime::Builder::new_current_thread()
17        .enable_io()
18        .enable_time()
19        .build()
20}
21
22impl Runtime {
23    /// Returns a new runtime initialized with default configuration values.
24    #[allow(clippy::new_ret_no_self)]
25    pub fn new() -> io::Result<Self> {
26        let rt = default_tokio_runtime()?;
27
28        Ok(Runtime {
29            rt,
30            local: LocalSet::new(),
31        })
32    }
33
34    /// Offload a future onto the single-threaded runtime.
35    ///
36    /// The returned join handle can be used to await the future's result.
37    ///
38    /// See [crate root][crate] documentation for more details.
39    ///
40    /// # Examples
41    /// ```
42    /// let rt = actix_rt::Runtime::new().unwrap();
43    ///
44    /// // Spawn a future onto the runtime
45    /// let handle = rt.spawn(async {
46    ///     println!("running on the runtime");
47    ///     42
48    /// });
49    ///
50    /// assert_eq!(rt.block_on(handle).unwrap(), 42);
51    /// ```
52    ///
53    /// # Panics
54    /// This function panics if the spawn fails. Failure occurs if the executor is currently at
55    /// capacity and is unable to spawn a new future.
56    #[track_caller]
57    pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
58    where
59        F: Future + 'static,
60    {
61        self.local.spawn_local(future)
62    }
63
64    /// Retrieves a reference to the underlying Tokio runtime associated with this instance.
65    ///
66    /// The Tokio runtime is responsible for executing asynchronous tasks and managing
67    /// the event loop for an asynchronous Rust program. This method allows accessing
68    /// the runtime to interact with its features directly.
69    ///
70    /// In a typical use case, you might need to share the same runtime between different
71    /// modules of your project. For example, a module might require a `tokio::runtime::Handle`
72    /// to spawn tasks on the same runtime, or the runtime itself to configure more complex
73    /// behaviours.
74    ///
75    /// # Example
76    ///
77    /// ```
78    /// use actix_rt::Runtime;
79    ///
80    /// mod module_a {
81    ///     pub fn do_something(handle: tokio::runtime::Handle) {
82    ///         handle.spawn(async {
83    ///             // Some asynchronous task here
84    ///         });
85    ///     }
86    /// }
87    ///
88    /// mod module_b {
89    ///     pub fn do_something_else(rt: &tokio::runtime::Runtime) {
90    ///         rt.spawn(async {
91    ///             // Another asynchronous task here
92    ///         });
93    ///     }
94    /// }
95    ///
96    /// let actix_runtime = actix_rt::Runtime::new().unwrap();
97    /// let tokio_runtime = actix_runtime.tokio_runtime();
98    ///
99    /// let handle = tokio_runtime.handle().clone();
100    ///
101    /// module_a::do_something(handle);
102    /// module_b::do_something_else(tokio_runtime);
103    /// ```
104    ///
105    /// # Returns
106    ///
107    /// An immutable reference to the `tokio::runtime::Runtime` instance associated with this
108    /// `Runtime` instance.
109    ///
110    /// # Note
111    ///
112    /// While this method provides an immutable reference to the Tokio runtime, which is safe to share across threads,
113    /// be aware that spawning blocking tasks on the Tokio runtime could potentially impact the execution
114    /// of the Actix runtime. This is because Tokio is responsible for driving the Actix system,
115    /// and blocking tasks could delay or deadlock other tasks in run loop.
116    pub fn tokio_runtime(&self) -> &tokio::runtime::Runtime {
117        &self.rt
118    }
119
120    /// Runs the provided future, blocking the current thread until the future completes.
121    ///
122    /// This function can be used to synchronously block the current thread until the provided
123    /// `future` has resolved either successfully or with an error. The result of the future is
124    /// then returned from this function call.
125    ///
126    /// Note that this function will also execute any spawned futures on the current thread, but
127    /// will not block until these other spawned futures have completed. Once the function returns,
128    /// any uncompleted futures remain pending in the `Runtime` instance. These futures will not run
129    /// until `block_on` or `run` is called again.
130    ///
131    /// The caller is responsible for ensuring that other spawned futures complete execution by
132    /// calling `block_on` or `run`.
133    #[track_caller]
134    pub fn block_on<F>(&self, f: F) -> F::Output
135    where
136        F: Future,
137    {
138        self.local.block_on(&self.rt, f)
139    }
140}
141
142impl From<tokio::runtime::Runtime> for Runtime {
143    fn from(rt: tokio::runtime::Runtime) -> Self {
144        Self {
145            local: LocalSet::new(),
146            rt,
147        }
148    }
149}