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::*;