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}