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}