native_executor/
lib.rs

1//! Platform-native async executor that bridges directly to OS event loops.
2//!
3//! Tasks use structured concurrency semantics: every `spawn*` call returns an
4//! [`AsyncTask`] handle, and dropping that handle cancels the task unless you
5//! awaited it or called [`AsyncTask::detach`] to explicitly opt into
6//! fire-and-forget execution.
7use std::{future::Future, time::Duration};
8
9#[cfg(target_vendor = "apple")]
10mod apple;
11use executor_core::{Executor, LocalExecutor, async_task::AsyncTask};
12
13#[cfg(target_os = "android")]
14pub mod android;
15#[cfg(target_arch = "wasm32")]
16mod web;
17
18#[cfg(any(
19    all(feature = "polyfill", not(target_arch = "wasm32")),
20    target_os = "android"
21))]
22pub mod polyfill;
23
24#[cfg(all(
25    not(feature = "polyfill"),
26    not(target_vendor = "apple"),
27    not(target_os = "android"),
28    not(target_arch = "wasm32")
29))]
30compile_error!(
31    "native-executor has no backend for this target; enable the `polyfill` feature \
32     to build on unsupported platforms."
33);
34
35/// Task execution priority levels for controlling scheduler behavior.
36///
37/// These priority levels map to platform-native scheduling priorities,
38/// allowing fine-grained control over task execution order and resource allocation.
39///
40/// # Platform notes
41/// - Apple (macOS/iOS/Catalyst) and wasm/web backends are ready to use with no extra setup.
42/// - Android **requires** calling [`register_android_main_thread`](crate::register_android_main_thread)
43///   on the real UI thread before any `spawn_main`/`spawn_main_local` usage, so the executor can
44///   dispatch tasks back to the platform main looper.
45/// - Polyfill backend (enabled via the `polyfill` feature on unsupported targets) needs you to
46///   create a dedicated thread and call [`polyfill::executor::start_main_executor`] there to
47///   simulate a main thread before using `spawn_main`/`spawn_local`.
48#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
49#[non_exhaustive]
50pub enum Priority {
51    /// Standard priority level for most application tasks.
52    ///
53    /// This is the default priority that provides balanced execution
54    /// suitable for general-purpose async operations.
55    #[default]
56    Default,
57    /// Lower priority for background tasks and non-critical operations.
58    ///
59    /// Background tasks yield CPU time to higher-priority tasks and are
60    /// ideal for operations like cleanup, logging, or data processing
61    /// that don't require immediate completion.
62    Background,
63    /// Higher priority for user-initiated tasks that require prompt execution.
64    /// This priority is suitable for tasks that directly impact user experience,
65    /// such as responding to user input or updating the UI.
66    UserInitiated,
67    /// Highest priority for tasks that require immediate attention to maintain
68    /// application responsiveness.
69    /// This priority should be reserved for critical operations that must
70    /// complete as soon as possible, such as rendering UI updates or handling
71    /// real-time data.
72    UserInteractive,
73    /// Lowest priority for tasks that can be deferred until the system is idle.
74    /// This priority is suitable for maintenance tasks, prefetching data,
75    /// or other operations that do not need to run immediately and can wait
76    /// until the system is less busy.
77    Utility,
78}
79
80trait PlatformExecutor {
81    type Timer: Future<Output = ()>;
82    fn with_priority(priority: Priority) -> Self;
83    fn sleep(duration: Duration) -> Self::Timer;
84    fn spawn<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
85    where
86        Fut: Future<Output: Send> + Send + 'static;
87    fn spawn_main<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
88    where
89        Fut: Future<Output: Send> + Send + 'static;
90    fn spawn_main_local<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
91    where
92        Fut: Future + 'static;
93}
94
95#[cfg(target_vendor = "apple")]
96type NativeExecutorInner = apple::AppleExecutor;
97
98#[cfg(target_os = "android")]
99type NativeExecutorInner = android::AndroidExecutor;
100
101#[cfg(target_arch = "wasm32")]
102type NativeExecutorInner = web::WebExecutor;
103
104#[cfg(all(
105    feature = "polyfill",
106    not(any(target_vendor = "apple", target_os = "android", target_arch = "wasm32"))
107))]
108type NativeExecutorInner = polyfill::executor::PolyfillExecutor;
109
110#[cfg(target_os = "android")]
111pub use android::register_android_main_thread;
112
113#[derive(Debug)]
114pub struct NativeExecutor(NativeExecutorInner);
115
116impl Default for NativeExecutor {
117    fn default() -> Self {
118        Self::new()
119    }
120}
121
122impl NativeExecutor {
123    #[must_use]
124    pub fn new() -> Self {
125        Self::with_priority(Priority::default())
126    }
127
128    #[must_use]
129    pub fn with_priority(priority: Priority) -> Self {
130        Self(<NativeExecutorInner as PlatformExecutor>::with_priority(
131            priority,
132        ))
133    }
134
135    pub fn spawn_main<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
136    where
137        Fut: Future<Output: Send> + Send + 'static,
138    {
139        <NativeExecutorInner as PlatformExecutor>::spawn_main(&self.0, fut)
140    }
141
142    pub fn spawn<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
143    where
144        Fut: Future<Output: Send> + Send + 'static,
145    {
146        <NativeExecutorInner as PlatformExecutor>::spawn(&self.0, fut)
147    }
148
149    pub fn spawn_main_local<Fut>(&self, fut: Fut) -> <Self as LocalExecutor>::Task<Fut::Output>
150    where
151        Fut: Future + 'static,
152    {
153        <NativeExecutorInner as PlatformExecutor>::spawn_main_local(&self.0, fut)
154    }
155}
156
157/// A timer that completes after a specified duration.
158#[derive(Debug)]
159pub struct NativeTimer(<NativeExecutorInner as PlatformExecutor>::Timer);
160
161impl NativeTimer {
162    #[must_use]
163    pub fn after(duration: Duration) -> Self {
164        Self(<NativeExecutorInner as PlatformExecutor>::sleep(duration))
165    }
166}
167
168impl Future for NativeTimer {
169    type Output = ();
170    fn poll(
171        mut self: std::pin::Pin<&mut Self>,
172        cx: &mut std::task::Context<'_>,
173    ) -> std::task::Poll<Self::Output> {
174        std::pin::pin!(&mut self.0).poll(cx)
175    }
176}
177
178impl Executor for NativeExecutor {
179    type Task<T: Send + 'static> = AsyncTask<T>;
180    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
181    where
182        Fut: Future<Output: Send> + Send + 'static,
183    {
184        <NativeExecutorInner as PlatformExecutor>::spawn(&self.0, fut)
185    }
186}
187
188/// # Panics
189/// It panics if not on main thread.
190impl LocalExecutor for NativeExecutor {
191    type Task<T: 'static> = AsyncTask<T>;
192    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
193    where
194        Fut: Future + 'static,
195    {
196        <NativeExecutorInner as PlatformExecutor>::spawn_main_local(&self.0, fut)
197    }
198}
199
200#[must_use]
201pub fn sleep(duration: Duration) -> NativeTimer {
202    NativeTimer::after(duration)
203}