executor_core/
lib.rs

1#![no_std]
2//! Write async libraries without choosing a runtime.
3//!
4//! Your users should decide whether to use tokio, async-std, or any other runtime.
5//! Not you.
6//!
7//! # How It Works
8//!
9//! Instead of hard-coding `tokio::spawn`, accept an executor parameter:
10//!
11//! ```rust
12//! use executor_core::Executor;
13//!
14//! pub async fn parallel_sum<E: Executor>(
15//!     executor: &E,
16//!     numbers: Vec<i32>
17//! ) -> i32 {
18//!     let (left, right) = numbers.split_at(numbers.len() / 2);
19//!     
20//!     // Spawn on ANY runtime via the executor parameter
21//!     let left_sum = executor.spawn(async move {
22//!         left.iter().sum::<i32>()
23//!     });
24//!     
25//!     let right_sum = executor.spawn(async move {
26//!         right.iter().sum::<i32>()
27//!     });
28//!     
29//!     left_sum.await + right_sum.await
30//! }
31//! ```
32//!
33//! Now users can call your library with their preferred runtime:
34//!
35//! ```rust,no_run
36//! # use executor_core::Executor;
37//! # async fn parallel_sum<E: Executor>(executor: &E, numbers: Vec<i32>) -> i32 { 0 }
38//!
39//! // User already using tokio? Great!
40//! # #[cfg(feature = "tokio")]
41//! tokio::runtime::Runtime::new().unwrap().block_on(async {
42//!     let runtime = tokio::runtime::Handle::current();
43//!     let sum = parallel_sum(&runtime, vec![1, 2, 3, 4]).await;
44//! });
45//!
46//! // User prefers async-executor? Also great!
47//! # #[cfg(feature = "async-executor")]  
48//! async_executor::Executor::new().run(async {
49//!     let executor = async_executor::Executor::new();
50//!     let sum = parallel_sum(&executor, vec![1, 2, 3, 4]).await;
51//! });
52//! ```
53//!
54//! # Quick Start
55//!
56//! **For library authors:** Just add the core crate, no features needed.
57//! ```toml
58//! [dependencies]
59//! executor-core = "0.2"
60//! ```
61//!
62//! **For app developers:** Add with your runtime's feature.
63//! ```toml
64//! [dependencies]
65//! executor-core = { version = "0.2", features = ["tokio"] }
66//! ```
67//!
68//! # API
69//!
70//! Two traits:
71//! - [`Executor`] - For `Send` futures
72//! - [`LocalExecutor`] - For non-`Send` futures (Rc, RefCell, etc.)
73//!
74//! Both return [`async_task::Task`]:
75//! ```rust,ignore
76//! let task = executor.spawn(async { work() });
77//! let result = task.await;        // Get result
78//! task.cancel().await;            // Cancel task
79//! task.detach();                  // Run in background
80//! ```
81//!
82extern crate alloc;
83use core::future::Future;
84
85#[cfg(feature = "std")]
86extern crate std;
87
88#[cfg(feature = "async-executor")]
89mod async_executor;
90#[cfg(feature = "tokio")]
91mod tokio;
92
93#[cfg(feature = "web")]
94pub mod web;
95
96/// A trait for executor implementations that can spawn thread-safe futures.
97///
98/// This trait represents executors capable of running concurrent tasks that are `Send`,
99/// meaning they can be safely moved between threads. Implementors provide the core
100/// functionality for task spawning in a multi-threaded context.
101pub trait Executor {
102    /// Spawns a thread-safe future on this executor.
103    ///
104    /// Returns an [`async_task::Task`] handle that can be used to:
105    /// - Await the result
106    /// - Cancel the task
107    /// - Detach the task to run in background
108    fn spawn<T: Send + 'static>(
109        &self,
110        fut: impl Future<Output = T> + Send + 'static,
111    ) -> async_task::Task<T>;
112}
113
114/// A trait for executor implementations that can spawn futures on the current thread.
115///
116/// This trait represents executors that operate within a single thread context,
117/// allowing them to work with futures that are not `Send`. This is essential for
118/// working with non-thread-safe types like [Rc](alloc::rc::Rc), [RefCell](core::cell::RefCell), or thread-local storage.
119pub trait LocalExecutor {
120    /// Spawns a future on this local executor.
121    ///
122    /// Returns an [`async_task::Task`] handle that can be used to:
123    /// - Await the result
124    /// - Cancel the task
125    /// - Detach the task to run in background
126    fn spawn<T: 'static>(&self, fut: impl Future<Output = T> + 'static) -> async_task::Task<T>;
127}
128
129pub use async_task::*;