native_executor/polyfill/
executor.rs

1#![allow(dead_code)]
2
3use async_task::{self as async_task_crate, Runnable};
4use executor_core::{Executor, async_task::AsyncTask};
5use futures_lite::future::block_on;
6use std::{
7    future::Future,
8    panic::catch_unwind,
9    sync::{Once, OnceLock},
10    thread,
11};
12
13use crate::{
14    PlatformExecutor,
15    polyfill::{assert_main_thread, register_main_thread, timer::PolyfillTimer},
16};
17
18/// Polyfill executor implementation using async-executor.
19///
20/// This executor spins up a pool of background worker threads and a synthetic
21/// "main thread" to emulate the APIs that platforms like Apple expose.
22/// Because it is not backed by a real OS event loop, its behavior differs
23/// from the native executors and should be considered a portability fallback.
24#[derive(Debug, Clone, Copy, Default)]
25pub struct PolyfillExecutor;
26
27static EXECUTOR: OnceLock<async_executor::Executor<'static>> = OnceLock::new();
28static WORKER_THREADS: Once = Once::new();
29
30fn global() -> &'static async_executor::Executor<'static> {
31    let executor = EXECUTOR.get_or_init(async_executor::Executor::new);
32    WORKER_THREADS.call_once(|| spawn_worker_threads(executor));
33    executor
34}
35
36fn spawn_worker_threads(executor: &'static async_executor::Executor<'static>) {
37    let num_threads = num_cpus::get().max(1);
38    for idx in 0..num_threads {
39        let thread_name = format!("native-executor::polyfill-{idx}");
40        let exec = executor;
41        thread::Builder::new()
42            .name(thread_name)
43            .spawn(move || run_worker_loop(exec))
44            .expect("failed to spawn polyfill worker thread");
45    }
46}
47
48fn run_worker_loop(executor: &'static async_executor::Executor<'static>) {
49    loop {
50        let _ = catch_unwind(|| {
51            block_on(executor.run(std::future::pending::<()>()));
52        });
53    }
54}
55
56static MAIN_EXECUTOR: OnceLock<async_executor::Executor<'static>> = OnceLock::new();
57
58/// Starts the synthetic "main thread" executor on the current thread.
59///
60/// Platforms that rely on the polyfill do not have an OS-provided main thread,
61/// so we create one by running this function on a thread of your choice. It
62/// blocks forever, driving tasks spawned via [`crate::spawn_main`] and
63/// [`crate::spawn_local`]. Most applications spawn a dedicated thread for this
64/// purpose when running on an unsupported target.
65///
66/// # Panics
67///
68/// Panics if the main executor has already been started.
69pub fn start_main_executor() {
70    register_main_thread();
71    let main_exec = async_executor::Executor::new();
72    MAIN_EXECUTOR
73        .set(main_exec)
74        .expect("Main executor already started");
75    let main_exec = MAIN_EXECUTOR
76        .get()
77        .expect("Unexpected error: main executor not set");
78    loop {
79        let _ = catch_unwind(|| {
80            block_on(main_exec.run(std::future::pending::<()>()));
81        });
82    }
83}
84
85fn main_executor() -> &'static async_executor::Executor<'static> {
86    MAIN_EXECUTOR.get().expect("polyfill main executor not started. Call `native_executor::polyfill::start_main_executor` on a dedicated thread before using `spawn_main` or `spawn_local`.")
87}
88
89impl PlatformExecutor for PolyfillExecutor {
90    type Timer = PolyfillTimer;
91    fn with_priority(_priority: crate::Priority) -> Self {
92        // PolyfillExecutor does not support priorities, so this is a no-op.
93        Self
94    }
95
96    fn spawn<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
97    where
98        Fut: Future<Output: Send> + Send + 'static,
99    {
100        global().spawn(fut).into()
101    }
102
103    fn spawn_main<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
104    where
105        Fut: Future<Output: Send> + Send + 'static,
106    {
107        main_executor().spawn(fut).into()
108    }
109
110    fn spawn_main_local<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
111    where
112        Fut: Future + 'static,
113    {
114        assert_main_thread("spawn_main_local");
115        let (runnable, task) = async_task_crate::spawn_local(fut, |runnable: Runnable| {
116            runnable.run();
117        });
118        runnable.run();
119        task.into()
120    }
121
122    fn sleep(duration: std::time::Duration) -> Self::Timer {
123        PolyfillTimer::after(duration)
124    }
125}
126
127impl Executor for PolyfillExecutor {
128    type Task<T: Send + 'static> = AsyncTask<T>;
129    fn spawn<Fut>(&self, fut: Fut) -> AsyncTask<Fut::Output>
130    where
131        Fut: Future<Output: Send> + Send + 'static,
132    {
133        global().spawn(fut).into()
134    }
135}