native_executor/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![warn(missing_docs, missing_debug_implementations)]
4extern crate alloc;
5
6#[cfg(target_vendor = "apple")]
7mod apple;
8
9#[cfg(all(not(target_vendor = "apple"), not(docsrs)))]
10compile_error!("native_executor currently only supports Apple platforms, more to come soon!");
11
12use async_task::Task;
13use executor_core::{Executor, LocalExecutor};
14pub mod mailbox;
15pub mod timer;
16use core::time::Duration;
17
18#[cfg(target_vendor = "apple")]
19pub use apple::ApplePlatformExecutor as NativeExecutor;
20
21mod unsupported {
22    use core::time::Duration;
23
24    use crate::{PlatformExecutor, Priority};
25
26    #[allow(unused)]
27    /// A stub executor for unsupported platforms that panics on use.
28    pub struct UnsupportedExecutor;
29    impl PlatformExecutor for UnsupportedExecutor {
30        fn exec_main(_f: impl FnOnce() + Send + 'static) {
31            panic!("exec_main is not supported on this platform");
32        }
33
34        fn exec(_f: impl FnOnce() + Send + 'static, _priority: Priority) {
35            panic!("exec is not supported on this platform");
36        }
37
38        fn exec_after(_delay: Duration, _f: impl FnOnce() + Send + 'static) {
39            panic!("exec_after is not supported on this platform");
40        }
41    }
42}
43
44#[cfg(not(target_vendor = "apple"))]
45/// The native executor implementation.
46pub use unsupported::UnsupportedExecutor as NativeExecutor;
47
48trait PlatformExecutor {
49    fn exec_main(f: impl FnOnce() + Send + 'static);
50    fn exec(f: impl FnOnce() + Send + 'static, priority: Priority);
51
52    fn exec_after(delay: Duration, f: impl FnOnce() + Send + 'static);
53}
54
55impl Executor for NativeExecutor {
56    fn spawn<T: Send + 'static>(&self, fut: impl Future<Output = T> + Send + 'static) -> Task<T> {
57        spawn(fut)
58    }
59}
60
61impl LocalExecutor for NativeExecutor {
62    fn spawn_local<T: 'static>(&self, fut: impl Future<Output = T> + 'static) -> Task<T> {
63        spawn_local(fut)
64    }
65}
66
67use async_task::Runnable;
68
69/// Task execution priority levels for controlling scheduler behavior.
70///
71/// These priority levels map to platform-native scheduling priorities,
72/// allowing fine-grained control over task execution order and resource allocation.
73#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
74#[non_exhaustive]
75pub enum Priority {
76    /// Standard priority level for most application tasks.
77    ///
78    /// This is the default priority that provides balanced execution
79    /// suitable for general-purpose async operations.
80    #[default]
81    Default,
82    /// Lower priority for background tasks and non-critical operations.
83    ///
84    /// Background tasks yield CPU time to higher-priority tasks and are
85    /// ideal for operations like cleanup, logging, or data processing
86    /// that don't require immediate completion.
87    Background,
88    /// Higher priority for user-initiated tasks that require prompt execution.
89    /// This priority is suitable for tasks that directly impact user experience,
90    /// such as responding to user input or updating the UI.
91    UserInitiated,
92    /// Highest priority for tasks that require immediate attention to maintain
93    /// application responsiveness.
94    /// This priority should be reserved for critical operations that must
95    /// complete as soon as possible, such as rendering UI updates or handling
96    /// real-time data.
97    UserInteractive,
98    /// Lowest priority for tasks that can be deferred until the system is idle.
99    /// This priority is suitable for maintenance tasks, prefetching data,
100    /// or other operations that do not need to run immediately and can wait
101    /// until the system is less busy.
102    Utility,
103}
104
105/// Creates a new task with the specified execution priority.
106///
107/// This allows fine-grained control over task scheduling, enabling
108/// background tasks to yield to higher-priority operations.
109///
110/// # Arguments
111/// * `future` - The future to execute asynchronously
112/// * `priority` - The scheduling priority for this task
113///
114/// # Returns
115/// A `Task` handle that can be awaited to retrieve the result
116///
117/// # Examples
118/// ```rust
119/// use native_executor::{spawn_with_priority, Priority};
120///
121/// // High-priority task for time-sensitive operations
122/// let urgent = spawn_with_priority(async {
123///     // Your time-sensitive work here
124///     42
125/// }, Priority::Default);
126///
127/// // Background task that won't interfere with UI responsiveness
128/// let cleanup = spawn_with_priority(async {
129///     // Your background work here
130///     "done"
131/// }, Priority::Background);
132/// ```
133pub fn spawn_with_priority<Fut>(future: Fut, priority: Priority) -> Task<Fut::Output>
134where
135    Fut: Future + Send + 'static,
136    Fut::Output: Send,
137{
138    let (runnable, task) = async_task::spawn(future, move |runnable: Runnable| {
139        NativeExecutor::exec(
140            move || {
141                runnable.run();
142            },
143            priority,
144        );
145    });
146
147    runnable.schedule();
148    task
149}
150
151/// Creates a new thread-local task that runs on the main thread.
152///
153/// This function is designed for futures that are not `Send` and must execute
154/// on the main thread.
155///
156/// # Arguments
157/// * `future` - The non-Send future to execute on the main thread
158///
159/// # Returns
160/// A `Task` handle that can be awaited to retrieve the result
161///
162/// # Panics
163/// This function may panic if not called from a main thread
164///
165/// # Examples
166/// ```rust
167/// use native_executor::spawn_local;
168/// use std::rc::Rc;
169///
170/// // Rc is not Send, so we need spawn_local
171/// let local_data = Rc::new(42);
172/// let task = spawn_local(async move {
173///     *local_data + 58
174/// });
175/// ```
176pub fn spawn_local<Fut>(future: Fut) -> Task<Fut::Output>
177where
178    Fut: Future + 'static,
179{
180    let (runnable, task) = async_task::spawn_local(future, move |runnable: Runnable| {
181        NativeExecutor::exec_main(move || {
182            runnable.run();
183        });
184    });
185
186    runnable.schedule();
187    task
188}
189
190/// Creates a new task with default priority.
191///
192/// This is the primary function for spawning async tasks. The task will be
193/// executed with default priority using platform-native scheduling.
194///
195/// # Arguments
196/// * `future` - The future to execute asynchronously
197///
198/// # Returns
199/// A `Task` handle that can be awaited to retrieve the result
200///
201/// # Examples
202/// ```rust
203/// use native_executor::spawn;
204///
205/// let task = spawn(async {
206///     // Your async work here
207///     42
208/// });
209/// ```
210pub fn spawn<Fut>(future: Fut) -> Task<Fut::Output>
211where
212    Fut: Future + Send + 'static,
213    Fut::Output: Send,
214{
215    spawn_with_priority(future, Priority::default())
216}
217
218/// Creates a new task that executes on the main thread.
219///
220/// This function schedules a `Send` future to run specifically on the main thread.
221/// This is useful for operations that must happen on the main thread, such as
222/// UI updates or accessing main-thread-only APIs.
223///
224/// # Arguments
225/// * `future` - The Send future to execute on the main thread
226///
227/// # Returns
228/// A `Task` handle that can be awaited to retrieve the result
229///
230/// # Examples
231/// ```rust
232/// use native_executor::spawn_main;
233///
234/// let task = spawn_main(async {
235///     // This runs on the main thread
236///     println!("Running on main thread");
237///     "done"
238/// });
239/// ```
240pub fn spawn_main<Fut>(future: Fut) -> Task<Fut::Output>
241where
242    Fut: Future + Send + 'static,
243    Fut::Output: Send,
244{
245    let (runnable, task) = async_task::spawn(future, move |runnable: Runnable| {
246        NativeExecutor::exec_main(move || {
247            runnable.run();
248        });
249    });
250
251    runnable.schedule();
252    task
253}