foyer_common/runtime.rs
1// Copyright 2025 foyer Project Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16 fmt::Debug,
17 future::Future,
18 mem::ManuallyDrop,
19 ops::{Deref, DerefMut},
20};
21
22use tokio::{
23 runtime::{Handle, Runtime},
24 task::JoinHandle,
25};
26
27/// A wrapper around [`Runtime`] that shuts down the runtime in the background when dropped.
28///
29/// This is necessary because directly dropping a nested runtime is not allowed in a parent runtime.
30pub struct BackgroundShutdownRuntime(ManuallyDrop<Runtime>);
31
32impl Debug for BackgroundShutdownRuntime {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.debug_tuple("BackgroundShutdownRuntime").finish()
35 }
36}
37
38impl Drop for BackgroundShutdownRuntime {
39 fn drop(&mut self) {
40 // Safety: The runtime is only dropped once here.
41 let runtime = unsafe { ManuallyDrop::take(&mut self.0) };
42
43 #[cfg(madsim)]
44 drop(runtime);
45 #[cfg(not(madsim))]
46 runtime.shutdown_background();
47 }
48}
49
50impl Deref for BackgroundShutdownRuntime {
51 type Target = Runtime;
52
53 fn deref(&self) -> &Self::Target {
54 &self.0
55 }
56}
57
58impl DerefMut for BackgroundShutdownRuntime {
59 fn deref_mut(&mut self) -> &mut Self::Target {
60 &mut self.0
61 }
62}
63
64impl From<Runtime> for BackgroundShutdownRuntime {
65 fn from(runtime: Runtime) -> Self {
66 Self(ManuallyDrop::new(runtime))
67 }
68}
69
70/// A non-cloneable runtime handle.
71#[derive(Debug)]
72pub struct SingletonHandle(Handle);
73
74impl From<Handle> for SingletonHandle {
75 fn from(handle: Handle) -> Self {
76 Self(handle)
77 }
78}
79
80impl SingletonHandle {
81 /// Spawns a future onto the Tokio runtime.
82 ///
83 /// This spawns the given future onto the runtime's executor, usually a
84 /// thread pool. The thread pool is then responsible for polling the future
85 /// until it completes.
86 ///
87 /// The provided future will start running in the background immediately
88 /// when `spawn` is called, even if you don't await the returned
89 /// `JoinHandle`.
90 ///
91 /// See [module level][mod] documentation for more details.
92 ///
93 /// [mod]: index.html
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use tokio::runtime::Runtime;
99 ///
100 /// # fn dox() {
101 /// // Create the runtime
102 /// let rt = Runtime::new().unwrap();
103 /// // Get a handle from this runtime
104 /// let handle = rt.handle();
105 ///
106 /// // Spawn a future onto the runtime using the handle
107 /// handle.spawn(async {
108 /// println!("now running on a worker thread");
109 /// });
110 /// # }
111 /// ```
112 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
113 where
114 F: Future + Send + 'static,
115 F::Output: Send + 'static,
116 {
117 self.0.spawn(future)
118 }
119
120 /// Runs the provided function on an executor dedicated to blocking
121 /// operations.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// use tokio::runtime::Runtime;
127 ///
128 /// # fn dox() {
129 /// // Create the runtime
130 /// let rt = Runtime::new().unwrap();
131 /// // Get a handle from this runtime
132 /// let handle = rt.handle();
133 ///
134 /// // Spawn a blocking function onto the runtime using the handle
135 /// handle.spawn_blocking(|| {
136 /// println!("now running on a worker thread");
137 /// });
138 /// # }
139 pub fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
140 where
141 F: FnOnce() -> R + Send + 'static,
142 R: Send + 'static,
143 {
144 self.0.spawn_blocking(func)
145 }
146
147 /// Runs a future to completion on this `Handle`'s associated `Runtime`.
148 ///
149 /// This runs the given future on the current thread, blocking until it is
150 /// complete, and yielding its resolved result. Any tasks or timers which
151 /// the future spawns internally will be executed on the runtime.
152 ///
153 /// When this is used on a `current_thread` runtime, only the
154 /// [`Runtime::block_on`] method can drive the IO and timer drivers, but the
155 /// `Handle::block_on` method cannot drive them. This means that, when using
156 /// this method on a `current_thread` runtime, anything that relies on IO or
157 /// timers will not work unless there is another thread currently calling
158 /// [`Runtime::block_on`] on the same runtime.
159 ///
160 /// # If the runtime has been shut down
161 ///
162 /// If the `Handle`'s associated `Runtime` has been shut down (through
163 /// [`Runtime::shutdown_background`], [`Runtime::shutdown_timeout`], or by
164 /// dropping it) and `Handle::block_on` is used it might return an error or
165 /// panic. Specifically IO resources will return an error and timers will
166 /// panic. Runtime independent futures will run as normal.
167 ///
168 /// # Panics
169 ///
170 /// This function panics if the provided future panics, if called within an
171 /// asynchronous execution context, or if a timer future is executed on a
172 /// runtime that has been shut down.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use tokio::runtime::Runtime;
178 ///
179 /// // Create the runtime
180 /// let rt = Runtime::new().unwrap();
181 ///
182 /// // Get a handle from this runtime
183 /// let handle = rt.handle();
184 ///
185 /// // Execute the future, blocking the current thread until completion
186 /// handle.block_on(async {
187 /// println!("hello");
188 /// });
189 /// ```
190 ///
191 /// Or using `Handle::current`:
192 ///
193 /// ```
194 /// use tokio::runtime::Handle;
195 ///
196 /// #[tokio::main]
197 /// async fn main () {
198 /// let handle = Handle::current();
199 /// std::thread::spawn(move || {
200 /// // Using Handle::block_on to run async code in the new thread.
201 /// handle.block_on(async {
202 /// println!("hello");
203 /// });
204 /// });
205 /// }
206 /// ```
207 ///
208 /// [`JoinError`]: struct@tokio::task::JoinError
209 /// [`JoinHandle`]: struct@tokio::task::JoinHandle
210 /// [`Runtime::block_on`]: fn@crate::runtime::Runtime::block_on
211 /// [`Runtime::shutdown_background`]: fn@tokio::runtime::Runtime::shutdown_background
212 /// [`Runtime::shutdown_timeout`]: fn@tokio::runtime::Runtime::shutdown_timeout
213 /// [`spawn_blocking`]: tokio::task::spawn_blocking
214 /// [`tokio::fs`]: tokio::fs
215 /// [`tokio::net`]: tokio::net
216 /// [`tokio::time`]: tokio::time
217 #[cfg(not(madsim))]
218 pub fn block_on<F: Future>(&self, future: F) -> F::Output {
219 self.0.block_on(future)
220 }
221
222 #[cfg(madsim)]
223 /// Dummy implementation for madsim.
224 pub fn block_on<F: Future>(&self, _: F) -> F::Output {
225 unimplemented!("`block_on()` is not supported with madsim")
226 }
227}