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