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}