Skip to main content

orb/
runtime.rs

1//! Runtime execution traits for async task management.
2//!
3//! This module defines the interface for spawning, executing, and managing
4//! asynchronous tasks across different runtime implementations.
5
6use std::future::Future;
7use std::sync::Arc;
8
9/// Trait for async runtime execution capabilities.
10///
11/// This trait defines the core execution operations that any async runtime
12/// should provide, including spawning tasks, running futures to completion,
13/// and detaching tasks.
14///
15/// # Example
16///
17/// ```rust
18/// use orb::prelude::*;
19/// use std::future::Future;
20///
21/// fn example<R: AsyncExec>(runtime: &R) -> impl Future<Output = ()> {
22///     async move {
23///         // Spawn a task
24///         let handle = runtime.spawn(async {
25///             // Do some async work
26///             42
27///         });
28///
29///         // Wait for the result
30///         let result = handle.await.unwrap();
31///         assert_eq!(result, 42);
32///     }
33/// }
34/// ```
35pub trait AsyncExec: AsyncExecDyn + Send + Sync + 'static {
36    type AsyncHandle<R: Send>: AsyncHandle<R>;
37
38    type ThreadHandle<R: Send>: ThreadHandle<R> + Send;
39
40    /// Spawn a task in the background, returning a handle to await its result.
41    ///
42    /// This method creates a new task that runs concurrently with the current
43    /// task. The returned handle can be used to wait for the task's completion
44    /// and retrieve its result.
45    ///
46    /// # NOTE:
47    ///
48    /// The return AsyncHandle adopts the behavior of tokio.
49    ///
50    /// The behavior of panic varies for runtimes:
51    /// - tokio will capture handle to task result,
52    /// - async-executor (smol) will not capture panic, the program will exit
53    ///
54    /// # Type Parameters
55    ///
56    /// * `F` - The future type to spawn
57    /// * `R` - The return type of the future
58    ///
59    /// # Parameters
60    ///
61    /// * `f` - The future to spawn
62    ///
63    /// # Returns
64    ///
65    /// A handle that implements [`AsyncHandle`] and can be used to await
66    /// the task's result.
67    ///
68    fn spawn<F, R>(&self, f: F) -> Self::AsyncHandle<R>
69    where
70        F: Future<Output = R> + Send + 'static,
71        R: Send + 'static;
72
73    /// Spawn a task and detach it (no handle returned).
74    ///
75    /// This method creates a new task that runs in the background without
76    /// providing a way to wait for its completion. The task will continue
77    /// running until it completes or the program exits.
78    ///
79    /// # NOTE:
80    ///
81    /// The behavior of panic varies for runtimes:
82    /// - tokio will ignore other tasks panic after detached,
83    /// - async-executor (smol) will not capture panic by default, the program will exit. There's a
84    ///   feature switch in [orb-smol](https://docs.rs/orb-smol) to change this behavior.
85    ///
86    /// # Type Parameters
87    ///
88    /// * `F` - The future type to spawn
89    /// * `R` - The return type of the future
90    ///
91    /// # Parameters
92    ///
93    /// * `f` - The future to spawn
94    fn spawn_detach<F, R>(&self, f: F)
95    where
96        F: Future<Output = R> + Send + 'static,
97        R: Send + 'static;
98
99    /// Run blocking code in a background thread pool, and return an async join handle
100    ///
101    /// # NOTE:
102    ///
103    /// This method spawn with threal pool provide by runtime in current context, globally.
104    /// In order for ResolveAddr job which does not have a AsyncExec handle, so this method is static.
105    ///
106    /// # Type Parameters
107    ///
108    /// * `F` - The future type to spawn
109    /// * `R` - The return type of the future
110    ///
111    /// # Parameters
112    ///
113    /// * `f` - The future to spawn
114    ///
115    /// # Returns
116    ///
117    /// A handle that implements [`ThreadHandle`] and can be used to await
118    /// the call result.
119    fn spawn_blocking<F, R>(f: F) -> Self::ThreadHandle<R>
120    where
121        F: FnOnce() -> R + Send + 'static,
122        R: Send + 'static;
123
124    /// Run a future to completion on the runtime.
125    ///
126    /// This method blocks the current thread until the provided future
127    /// completes, returning its result.
128    ///
129    /// # Type Parameters
130    ///
131    /// * `F` - The future type to run
132    /// * `R` - The return type of the future
133    ///
134    /// # Parameters
135    ///
136    /// * `f` - The future to run to completion
137    ///
138    /// # Returns
139    ///
140    /// The output of the future when it completes.
141    fn block_on<F, R>(&self, f: F) -> R
142    where
143        F: Future<Output = R> + Send,
144        R: Send + 'static;
145
146    fn to_dyn(self) -> Arc<dyn AsyncExecDyn>
147    where
148        Self: Sized,
149    {
150        Arc::new(self)
151    }
152}
153
154pub trait AsyncExecDyn: Send + Sync + 'static {
155    /// Spawn a task and detach it (no handle returned).
156    ///
157    /// This method creates a new task that runs in the background without
158    /// providing a way to wait for its completion. The task will continue
159    /// running until it completes or the program exits.
160    ///
161    /// # NOTE:
162    ///
163    /// The behavior of panic varies for runtimes:
164    /// - tokio will ignore other tasks panic after detached,
165    /// - async-executor (smol) will not capture panic by default, the program will exit. There's a
166    ///   feature switch in [orb-smol](https://docs.rs/orb-smol) to change this behavior.
167    ///
168    /// # Type Parameters
169    ///
170    /// * `F` - The future type to spawn
171    /// * `R` - The return type of the future
172    ///
173    /// # Parameters
174    ///
175    /// * `f` - The future to spawn
176    fn spawn_detach_dyn(&self, f: Box<dyn Future<Output = ()> + Send + Unpin>);
177}
178
179impl<FT: std::ops::Deref<Target = T> + Send + Sync + 'static, T: AsyncExec> AsyncExecDyn for FT {
180    #[inline]
181    fn spawn_detach_dyn(&self, f: Box<dyn Future<Output = ()> + Send + Unpin>) {
182        T::spawn_detach_dyn(self.deref(), f)
183    }
184}
185
186impl<FT: std::ops::Deref<Target = T> + Send + Sync + 'static, T: AsyncExec> AsyncExec for FT {
187    type AsyncHandle<R: Send> = T::AsyncHandle<R>;
188
189    type ThreadHandle<R: Send> = T::ThreadHandle<R>;
190
191    #[inline(always)]
192    fn spawn<F, R>(&self, f: F) -> Self::AsyncHandle<R>
193    where
194        F: Future<Output = R> + Send + 'static,
195        R: Send + 'static,
196    {
197        T::spawn(self.deref(), f)
198    }
199
200    #[inline(always)]
201    fn spawn_detach<F, R>(&self, f: F)
202    where
203        F: Future<Output = R> + Send + 'static,
204        R: Send + 'static,
205    {
206        T::spawn_detach(self.deref(), f)
207    }
208
209    #[inline(always)]
210    fn spawn_blocking<F, R>(f: F) -> Self::ThreadHandle<R>
211    where
212        F: FnOnce() -> R + Send + 'static,
213        R: Send + 'static,
214    {
215        T::spawn_blocking(f)
216    }
217
218    #[inline(always)]
219    fn block_on<F, R>(&self, f: F) -> R
220    where
221        F: Future<Output = R> + Send,
222        R: Send + 'static,
223    {
224        T::block_on(self, f)
225    }
226}
227
228/// A handle for managing spawned async tasks.
229///
230/// This trait provides methods for waiting for a task's completion or
231/// detaching it to run in the background.
232///
233/// # NOTE:
234///
235/// The behavior of dropping a AsyncHandle should be detach, we adopt this behavior because
236/// user is more familiar with tokio's behavior. We don't want bugs when dropping the task handle unnoticed.
237///
238/// # Type Parameters
239///
240/// * `T` - The return type of the task
241///
242/// # Returns
243///
244/// A future that resolves to `Ok(T)` if the task completed successfully,
245/// or `Err(())` if the task panics.
246pub trait AsyncHandle<T>: Future<Output = Result<T, ()>> + Send + Unpin {
247    /// Whether a task can be join immediately
248    fn is_finished(&self) -> bool;
249
250    /// Detach the task to run in the background without waiting for its result.
251    ///
252    /// After calling this method, the task will continue running until it
253    /// completes or until its runtime dropped.
254    fn detach(self)
255    where
256        Self: Sized;
257
258    /// Abort the task execution, don't care for it's result
259    fn abort(self)
260    where
261        Self: Sized;
262
263    fn detach_boxed(self: Box<Self>);
264
265    fn abort_boxed(self: Box<Self>);
266}
267
268/// A handle for spawn_blocking()
269///
270/// This trait provides methods for waiting for a blocking task's completion or
271/// detaching it to run in the background.
272///
273/// Calling await on the ThreadHandle will get Result<T, ()>.
274///
275/// # NOTE:
276///
277/// The behavior of dropping a ThreadHandle will not abort the task (since it run as pthread)
278///
279/// # Type Parameters
280///
281/// * `T` - The return type of the task
282///
283/// # Returns
284///
285/// A future that resolves to `Ok(T)` if the task completed successfully,
286/// or `Err(())` if the task panics.
287pub trait ThreadHandle<T>: Future<Output = Result<T, ()>> {
288    /// Whether a task can be join immediately
289    fn is_finished(&self) -> bool;
290}