Skip to main content

actix_rt/
runtime.rs

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