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}