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}