|
32 | 32 | use std::{future::Future, pin::Pin, sync::OnceLock}; |
33 | 33 | use thiserror::Error; |
34 | 34 |
|
35 | | -pub(crate) type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>; |
36 | | -pub(crate) type PinnedLocalFuture<T> = Pin<Box<dyn Future<Output = T>>>; |
| 35 | +/// A future that has been pinned. |
| 36 | +pub type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>; |
| 37 | +/// A future that has been pinned. |
| 38 | +pub type PinnedLocalFuture<T> = Pin<Box<dyn Future<Output = T>>>; |
37 | 39 |
|
38 | 40 | static SPAWN: OnceLock<fn(PinnedFuture<()>)> = OnceLock::new(); |
39 | 41 | static SPAWN_LOCAL: OnceLock<fn(PinnedLocalFuture<()>)> = OnceLock::new(); |
@@ -284,63 +286,42 @@ impl Executor { |
284 | 286 | .map_err(|_| ExecutorError::AlreadySet)?; |
285 | 287 | Ok(()) |
286 | 288 | } |
287 | | -} |
288 | 289 |
|
289 | | -#[cfg(test)] |
290 | | -mod tests { |
291 | | - #[cfg(feature = "futures-executor")] |
292 | | - #[test] |
293 | | - fn can_spawn_local_future() { |
294 | | - use crate::Executor; |
295 | | - use std::rc::Rc; |
296 | | - _ = Executor::init_futures_executor(); |
297 | | - let rc = Rc::new(()); |
298 | | - Executor::spawn_local(async { |
299 | | - _ = rc; |
300 | | - }); |
301 | | - Executor::spawn(async {}); |
302 | | - } |
| 290 | + /// Globally sets a custom executor as the executor used to spawn tasks. |
| 291 | + /// |
| 292 | + /// Returns `Err(_)` if an executor has already been set. |
| 293 | + pub fn init_custom_executor( |
| 294 | + custom_executor: impl CustomExecutor + 'static, |
| 295 | + ) -> Result<(), ExecutorError> { |
| 296 | + static EXECUTOR: OnceLock<Box<dyn CustomExecutor>> = OnceLock::new(); |
| 297 | + EXECUTOR |
| 298 | + .set(Box::new(custom_executor)) |
| 299 | + .map_err(|_| ExecutorError::AlreadySet)?; |
303 | 300 |
|
304 | | - #[cfg(feature = "futures-executor")] |
305 | | - #[test] |
306 | | - fn can_make_threaded_progress() { |
307 | | - use crate::Executor; |
308 | | - use std::sync::{atomic::AtomicUsize, Arc}; |
309 | | - _ = Executor::init_futures_executor(); |
310 | | - let counter = Arc::new(AtomicUsize::new(0)); |
311 | | - Executor::spawn({ |
312 | | - let counter = Arc::clone(&counter); |
313 | | - async move { |
314 | | - assert_eq!( |
315 | | - counter.fetch_add(1, std::sync::atomic::Ordering::AcqRel), |
316 | | - 0 |
317 | | - ); |
318 | | - } |
319 | | - }); |
320 | | - futures::executor::block_on(Executor::tick()); |
321 | | - assert_eq!(counter.load(std::sync::atomic::Ordering::Acquire), 1); |
| 301 | + SPAWN |
| 302 | + .set(|fut| { |
| 303 | + EXECUTOR.get().unwrap().spawn(fut); |
| 304 | + }) |
| 305 | + .map_err(|_| ExecutorError::AlreadySet)?; |
| 306 | + SPAWN_LOCAL |
| 307 | + .set(|fut| EXECUTOR.get().unwrap().spawn_local(fut)) |
| 308 | + .map_err(|_| ExecutorError::AlreadySet)?; |
| 309 | + POLL_LOCAL |
| 310 | + .set(|| EXECUTOR.get().unwrap().poll_local()) |
| 311 | + .map_err(|_| ExecutorError::AlreadySet)?; |
| 312 | + Ok(()) |
322 | 313 | } |
| 314 | +} |
323 | 315 |
|
324 | | - #[cfg(feature = "futures-executor")] |
325 | | - #[test] |
326 | | - fn can_make_local_progress() { |
327 | | - use crate::Executor; |
328 | | - use std::sync::{atomic::AtomicUsize, Arc}; |
329 | | - _ = Executor::init_futures_executor(); |
330 | | - let counter = Arc::new(AtomicUsize::new(0)); |
331 | | - Executor::spawn_local({ |
332 | | - let counter = Arc::clone(&counter); |
333 | | - async move { |
334 | | - assert_eq!( |
335 | | - counter.fetch_add(1, std::sync::atomic::Ordering::AcqRel), |
336 | | - 0 |
337 | | - ); |
338 | | - Executor::spawn_local(async { |
339 | | - // Should not crash |
340 | | - }); |
341 | | - } |
342 | | - }); |
343 | | - Executor::poll_local(); |
344 | | - assert_eq!(counter.load(std::sync::atomic::Ordering::Acquire), 1); |
345 | | - } |
| 316 | +/// A trait for custom executors. |
| 317 | +/// Custom executors can be used to integrate with any executor that supports spawning futures. |
| 318 | +/// |
| 319 | +/// All methods can be called recursively. |
| 320 | +pub trait CustomExecutor: Send + Sync { |
| 321 | + /// Spawns a future, usually on a thread pool. |
| 322 | + fn spawn(&self, fut: PinnedFuture<()>); |
| 323 | + /// Spawns a local future. May require calling `poll_local` to make progress. |
| 324 | + fn spawn_local(&self, fut: PinnedLocalFuture<()>); |
| 325 | + /// Polls the executor, if it supports polling. |
| 326 | + fn poll_local(&self); |
346 | 327 | } |
0 commit comments