executor_core/
lib.rs

1//! # executor-core
2//!
3//! A flexible task executor abstraction layer for Rust async runtimes.
4//!
5//! This crate provides unified traits and type-erased wrappers for different async executors,
6//! allowing you to write code that's agnostic to the underlying executor implementation.
7//!
8//! ## Overview
9//!
10//! The crate is built around two main traits:
11//! - [`Executor`]: For spawning `Send + 'static` futures
12//! - [`LocalExecutor`]: For spawning `'static` futures (not necessarily `Send`)
13//!
14//! Both traits produce tasks that implement the [`Task`] trait, providing:
15//! - [`Future`] implementation for awaiting results
16//! - [`Task::poll_result`] for explicit error handling
17//! - [`Task::detach`] for fire-and-forget tasks
18//!
19//!
20//! ## Features
21//!
22//! - **Zero-cost Executor Abstraction**: Unified [`Executor`] and [`LocalExecutor`] traits
23//!   using Generic Associated Types (GAT) to prevent unnecessary heap allocation and dynamic dispatch
24//! - **Type Erasure**: [`AnyExecutor`] and [`AnyLocalExecutor`] for runtime flexibility
25//! - **Task Management**: Panic-aware [`Task::result`] and background execution with [`Task::detach`]
26//! - **No-std Compatible**: Core functionality works in no-std environments
27//! - **Panic Safety**: Proper panic handling and propagation
28//!
29//! ## Lifetime Constraints
30//!
31//! The current API requires `'static` lifetimes for both futures and their outputs.
32//! This constraint comes from the underlying async runtimes and ensures memory safety
33//! when tasks may outlive their spawning scope. While this limits flexibility, it
34//! matches the constraints of most async runtime implementations in Rust.
35
36#![no_std]
37#![cfg_attr(docsrs, feature(doc_cfg))]
38#![warn(missing_docs, missing_debug_implementations)]
39
40#[cfg(feature = "async-task")]
41#[cfg_attr(docsrs, doc(cfg(feature = "async-task")))]
42pub mod async_task;
43
44#[cfg(feature = "async-executor")]
45#[cfg_attr(docsrs, doc(cfg(feature = "async-executor")))]
46pub mod async_executor;
47
48#[cfg(feature = "tokio")]
49#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
50pub mod tokio;
51
52use core::{
53    any::Any,
54    fmt::Debug,
55    future::{Future, poll_fn},
56    marker::PhantomData,
57    panic::AssertUnwindSafe,
58    pin::Pin,
59    task::{Context, Poll},
60};
61
62pub mod mailbox;
63
64use alloc::boxed::Box;
65use async_channel::Receiver;
66
67extern crate alloc;
68
69/// A trait for spawning `Send + 'static` futures.
70///
71/// This trait is implemented by runtime-agnostic executors that can spawn futures
72/// across thread boundaries. The spawned futures must be `Send` and `'static`.
73///
74/// The `'static` lifetime requirements come from the underlying async runtimes
75/// (like Tokio) which need to ensure memory safety when tasks are moved across
76/// threads and may outlive their spawning scope.
77///
78/// See [AnyExecutor] for a type-erased executor.
79pub trait Executor: Send + Sync {
80    /// The task type returned by [`spawn`](Self::spawn).
81    ///
82    /// The `T: Send + 'static` constraint ensures the task output can be safely
83    /// sent across thread boundaries and doesn't contain any borrowed data.
84    type Task<T: Send + 'static>: Task<T> + Send;
85
86    /// Spawn a future that will run to completion.
87    ///
88    /// The future must be `Send + 'static` to ensure it can be moved across threads.
89    /// Returns a [`Task`] that can be awaited to get the result.
90    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
91    where
92        Fut: Future<Output: Send> + Send + 'static;
93}
94
95impl<E: Executor> Executor for &E {
96    type Task<T: Send + 'static> = E::Task<T>;
97
98    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
99    where
100        Fut: Future<Output: Send> + Send + 'static,
101    {
102        (*self).spawn(fut)
103    }
104}
105
106impl<E: Executor> Executor for &mut E {
107    type Task<T: Send + 'static> = E::Task<T>;
108
109    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
110    where
111        Fut: Future<Output: Send> + Send + 'static,
112    {
113        (**self).spawn(fut)
114    }
115}
116
117impl<E: Executor> Executor for Box<E> {
118    type Task<T: Send + 'static> = E::Task<T>;
119
120    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
121    where
122        Fut: Future<Output: Send> + Send + 'static,
123    {
124        (**self).spawn(fut)
125    }
126}
127
128impl<E: Executor> Executor for alloc::sync::Arc<E> {
129    type Task<T: Send + 'static> = E::Task<T>;
130
131    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
132    where
133        Fut: Future<Output: Send> + Send + 'static,
134    {
135        (**self).spawn(fut)
136    }
137}
138
139/// A trait for spawning `'static` futures that may not be `Send`.
140///
141/// This trait is for executors that can spawn futures that don't need to be `Send`,
142/// typically single-threaded executors or local task spawners.
143///
144/// The `'static` lifetime requirements come from the underlying async runtimes
145/// which need to ensure memory safety when tasks may outlive their spawning scope,
146/// even in single-threaded contexts.
147///
148/// See [AnyLocalExecutor] for a type-erased local executor.
149pub trait LocalExecutor {
150    /// The task type returned by [`spawn`](Self::spawn).
151    ///
152    /// The `T: 'static` constraint ensures the task output doesn't contain
153    /// any borrowed data that could become invalid.
154    type Task<T: 'static>: Task<T>;
155
156    /// Spawn a future that will run to completion on the local executor.
157    ///
158    /// The future must be `'static` but does not need to be `Send`.
159    /// Returns a [`Task`] that can be awaited to get the result.
160    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
161    where
162        Fut: Future + 'static;
163}
164
165impl<E: LocalExecutor> LocalExecutor for &E {
166    type Task<T: 'static> = E::Task<T>;
167
168    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
169    where
170        Fut: Future + 'static,
171    {
172        (*self).spawn_local(fut)
173    }
174}
175
176impl<E: LocalExecutor> LocalExecutor for &mut E {
177    type Task<T: 'static> = E::Task<T>;
178
179    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
180    where
181        Fut: Future + 'static,
182    {
183        (**self).spawn_local(fut)
184    }
185}
186
187impl<E: LocalExecutor> LocalExecutor for Box<E> {
188    type Task<T: 'static> = E::Task<T>;
189
190    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
191    where
192        Fut: Future + 'static,
193    {
194        (**self).spawn_local(fut)
195    }
196}
197
198impl<E: LocalExecutor> LocalExecutor for alloc::rc::Rc<E> {
199    type Task<T: 'static> = E::Task<T>;
200
201    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
202    where
203        Fut: Future + 'static,
204    {
205        (**self).spawn_local(fut)
206    }
207}
208
209impl<E: LocalExecutor> LocalExecutor for alloc::sync::Arc<E> {
210    type Task<T: 'static> = E::Task<T>;
211
212    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
213    where
214        Fut: Future + 'static,
215    {
216        (**self).spawn_local(fut)
217    }
218}
219
220trait AnyLocalExecutorImpl: 'static + Any {
221    fn spawn_local_boxed(
222        &self,
223        fut: Pin<Box<dyn Future<Output = ()>>>,
224    ) -> Pin<Box<dyn Task<()> + 'static>>;
225    fn as_any(&self) -> &dyn Any;
226}
227
228impl<E> AnyLocalExecutorImpl for E
229where
230    E: LocalExecutor + 'static,
231{
232    fn spawn_local_boxed(
233        &self,
234        fut: Pin<Box<dyn Future<Output = ()>>>,
235    ) -> Pin<Box<dyn Task<()> + 'static>> {
236        let task = self.spawn_local(fut);
237        Box::pin(task)
238    }
239
240    fn as_any(&self) -> &dyn Any {
241        self
242    }
243}
244
245/// A type-erased [`LocalExecutor`] that can hold any local executor implementation.
246///
247/// This allows for runtime selection of executors and storing different executor
248/// types in the same collection.
249pub struct AnyLocalExecutor(Box<dyn AnyLocalExecutorImpl>);
250
251impl Debug for AnyLocalExecutor {
252    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
253        f.debug_struct("AnyLocalExecutor").finish()
254    }
255}
256
257impl Debug for AnyExecutor {
258    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
259        f.debug_struct("AnyExecutor").finish()
260    }
261}
262
263impl<T> dyn Task<T> {
264    /// Get the result of the boxed task, including any errors that occurred.
265    ///
266    /// This method awaits the task completion and returns a [`Result`] that
267    /// allows you to handle panics and other errors explicitly.
268    pub async fn result(self: Box<Self>) -> Result<T, Error> {
269        let mut pinned: Pin<Box<Self>> = self.into();
270
271        poll_fn(move |cx| pinned.as_mut().poll_result(cx)).await
272    }
273}
274
275impl AnyExecutor {
276    /// Create a new [`AnyExecutor`] wrapping the given executor.
277    pub fn new(executor: impl Executor + 'static) -> Self {
278        Self(Box::new(executor))
279    }
280
281    /// Attempt to downcast to a concrete executor type by reference.
282    ///
283    /// Returns `Some(&E)` if the underlying executor is of type `E`, `None` otherwise.
284    pub fn downcast_ref<E: Executor + 'static>(&self) -> Option<&E> {
285        self.0.as_any().downcast_ref()
286    }
287
288    /// Attempt to downcast to a concrete executor type by value.
289    ///
290    /// Returns `Ok(Box<E>)` if the underlying executor is of type `E`,
291    /// `Err(Self)` otherwise (returning the original `AnyExecutor`).
292    pub fn downcast<E: Executor + 'static>(self) -> Result<Box<E>, Self> {
293        if let Some(executor) = self.0.as_any().downcast_ref::<E>() {
294            let ptr: *const E = executor;
295            // Safety: we just confirmed the type
296            let boxed: Box<E> = unsafe { Box::from_raw(ptr as *mut E) };
297            core::mem::forget(self);
298            Ok(boxed)
299        } else {
300            Err(self)
301        }
302    }
303}
304
305impl AnyLocalExecutor {
306    /// Create a new [`AnyLocalExecutor`] wrapping the given local executor.
307    pub fn new(executor: impl LocalExecutor + 'static) -> Self {
308        Self(Box::new(executor))
309    }
310
311    /// Attempt to downcast to a concrete local executor type by reference.
312    ///
313    /// Returns `Some(&E)` if the underlying executor is of type `E`, `None` otherwise.
314    pub fn downcast_ref<E: LocalExecutor + 'static>(&self) -> Option<&E> {
315        self.0.as_any().downcast_ref()
316    }
317
318    /// Attempt to downcast to a concrete local executor type by value.
319    ///
320    /// Returns `Ok(Box<E>)` if the underlying executor is of type `E`,
321    /// `Err(Self)` otherwise (returning the original `AnyLocalExecutor`).
322    pub fn downcast<E: LocalExecutor + 'static>(self) -> Result<Box<E>, Self> {
323        if let Some(executor) = self.0.as_any().downcast_ref::<E>() {
324            let ptr: *const E = executor;
325            // Safety: type checked above
326            let boxed: Box<E> = unsafe { Box::from_raw(ptr as *mut E) };
327            core::mem::forget(self);
328            Ok(boxed)
329        } else {
330            Err(self)
331        }
332    }
333}
334
335/// Task type returned by [`AnyLocalExecutor`].
336///
337/// This task can be awaited like any other task and provides the same
338/// cancellation and error handling capabilities as other task implementations.
339/// It wraps tasks from any [`LocalExecutor`] implementation in a type-erased manner.
340pub struct AnyLocalExecutorTask<T> {
341    inner: Pin<Box<dyn Task<()> + 'static>>,
342    receiver: Receiver<Result<T, Error>>,
343}
344
345impl<T> AnyLocalExecutorTask<T> {
346    /// Create a new `AnyLocalExecutorTask` wrapping the given inner task and receiver.
347    fn new(inner: Pin<Box<dyn Task<()> + 'static>>, receiver: Receiver<Result<T, Error>>) -> Self {
348        Self { inner, receiver }
349    }
350
351    /// Get the result of the task, including any errors that occurred.
352    pub async fn result(self) -> Result<T, Error> {
353        <Self as Task<T>>::result(self).await
354    }
355
356    /// Detach the task, allowing it to run in the background without being awaited.
357    pub fn detach(self) {
358        <Self as Task<T>>::detach(self)
359    }
360}
361
362impl<T> core::fmt::Debug for AnyLocalExecutorTask<T> {
363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        f.debug_struct("AnyLocalExecutorTask")
365            .finish_non_exhaustive()
366    }
367}
368
369impl<T> Future for AnyLocalExecutorTask<T> {
370    type Output = T;
371
372    fn poll(
373        self: Pin<&mut Self>,
374        cx: &mut core::task::Context<'_>,
375    ) -> core::task::Poll<Self::Output> {
376        self.poll_result(cx).map(|res| res.unwrap())
377    }
378}
379
380impl<T> Task<T> for AnyLocalExecutorTask<T> {
381    fn poll_result(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<T, Error>> {
382        // First, ensure the underlying task is being polled
383        let this = unsafe { self.get_unchecked_mut() };
384        let _ = this.inner.as_mut().poll(cx);
385
386        // Then poll the receiver
387        let mut recv = this.receiver.recv();
388        unsafe {
389            Pin::new_unchecked(&mut recv)
390                .poll(cx)
391                .map(|res| res.unwrap_or_else(|_| Err(Box::new("Channel closed"))))
392        }
393    }
394}
395
396impl LocalExecutor for AnyLocalExecutor {
397    type Task<T: 'static> = AnyLocalExecutorTask<T>;
398
399    fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
400    where
401        Fut: Future + 'static,
402    {
403        let (sender, receiver) = async_channel::bounded(1);
404        let fut = async move {
405            let res = AssertUnwindSafe(fut).await;
406            let _ = sender.send(Ok(res)).await;
407        };
408        let inner = self.0.spawn_local_boxed(Box::pin(fut));
409        AnyLocalExecutorTask::new(inner, receiver)
410    }
411}
412
413/// Type alias for errors that can occur during task execution.
414///
415/// This represents panics or other unrecoverable errors from spawned tasks.
416type Error = Box<dyn core::any::Any + Send>;
417
418/// A trait representing a spawned task that can be awaited or queried for results.
419///
420/// This trait extends [`Future`] with additional capabilities for task management:
421/// - Explicit error handling via [`poll_result`](Self::poll_result)
422/// - Convenience methods for getting results and detaching
423///
424/// Dropping a task cancels its execution.
425pub trait Task<T>: Future<Output = T> {
426    /// Poll the task for completion, returning a [`Result`] that can contain errors.
427    ///
428    /// Unlike the [`Future::poll`] implementation, this method allows you to handle
429    /// task panics and other errors explicitly rather than propagating them.
430    fn poll_result(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<T, Error>>;
431
432    /// Get the result of the task, including any errors that occurred.
433    ///
434    /// This is equivalent to awaiting the task but returns a [`Result`] that
435    /// allows you to handle panics and other errors explicitly.
436    ///
437    fn result(self) -> impl Future<Output = Result<T, Error>>
438    where
439        Self: Sized,
440    {
441        ResultFuture {
442            task: self,
443            _phantom: PhantomData,
444        }
445    }
446
447    /// Detach the task, allowing it to run in the background without being awaited.
448    ///
449    /// Once detached, the task will continue running but its result cannot be retrieved.
450    /// This is useful for fire-and-forget operations where you don't need to wait for
451    /// or handle the result.
452    fn detach(self)
453    where
454        Self: Sized,
455    {
456        core::mem::forget(self);
457    }
458}
459
460/// Future returned by [`Task::result()`].
461///
462/// This future resolves to a `Result<T, Error>` when the underlying task completes,
463/// allowing explicit handling of task panics and other errors without propagating them.
464pub struct ResultFuture<T: Task<U>, U> {
465    task: T,
466    _phantom: PhantomData<U>,
467}
468
469impl<T: Task<U>, U> core::fmt::Debug for ResultFuture<T, U> {
470    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
471        f.debug_struct("ResultFuture").finish_non_exhaustive()
472    }
473}
474
475impl<T: Task<U>, U> Future for ResultFuture<T, U> {
476    type Output = Result<U, Error>;
477
478    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
479        let this = unsafe { self.get_unchecked_mut() };
480        unsafe { Pin::new_unchecked(&mut this.task) }.poll_result(cx)
481    }
482}
483
484/// A type-erased [`Executor`] that can hold any executor implementation.
485///
486/// This allows for runtime selection of executors and storing different executor
487/// types in the same collection.
488///
489pub struct AnyExecutor(Box<dyn AnyExecutorImpl>);
490
491/// Task type returned by [`AnyExecutor`].
492///
493/// This task can be awaited like any other task and provides the same
494/// cancellation and error handling capabilities.
495pub struct AnyExecutorTask<T> {
496    inner: Pin<Box<dyn Task<()> + Send>>,
497    receiver: Receiver<Result<T, Error>>,
498}
499
500impl<T: Send> AnyExecutorTask<T> {
501    /// Create a new `AnyExecutorTask` wrapping the given inner task and receiver.
502    fn new(inner: Pin<Box<dyn Task<()> + Send>>, receiver: Receiver<Result<T, Error>>) -> Self {
503        Self { inner, receiver }
504    }
505
506    /// Get the result of the task, including any errors that occurred.
507    ///
508    /// This is equivalent to awaiting the task but returns a [`Result`] that
509    /// allows you to handle panics and other errors explicitly.
510    pub async fn result(self) -> Result<T, Error> {
511        <Self as Task<T>>::result(self).await
512    }
513
514    /// Detach the task, allowing it to run in the background without being awaited.
515    pub fn detach(self) {
516        <Self as Task<T>>::detach(self)
517    }
518}
519
520impl<T> core::fmt::Debug for AnyExecutorTask<T> {
521    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
522        f.debug_struct("AnyExecutorTask").finish_non_exhaustive()
523    }
524}
525
526impl<T: Send> Future for AnyExecutorTask<T> {
527    type Output = T;
528
529    fn poll(
530        self: Pin<&mut Self>,
531        cx: &mut core::task::Context<'_>,
532    ) -> core::task::Poll<Self::Output> {
533        self.poll_result(cx).map(|res| res.unwrap())
534    }
535}
536
537impl<T: Send> Task<T> for AnyExecutorTask<T> {
538    fn poll_result(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<T, Error>> {
539        // First, ensure the underlying task is being polled
540        let this = unsafe { self.get_unchecked_mut() };
541        let _ = this.inner.as_mut().poll(cx);
542
543        // Then poll the receiver
544        let mut recv = this.receiver.recv();
545        unsafe {
546            Pin::new_unchecked(&mut recv)
547                .poll(cx)
548                .map(|res| res.unwrap_or_else(|_| Err(Box::new("Channel closed"))))
549        }
550    }
551}
552
553impl Executor for AnyExecutor {
554    type Task<T: Send + 'static> = AnyExecutorTask<T>;
555
556    fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
557    where
558        Fut: Future<Output: Send> + Send + 'static,
559    {
560        let (sender, receiver) = async_channel::bounded(1);
561        let fut = async move {
562            let res = AssertUnwindSafe(fut).await;
563            let _ = sender.send(Ok(res)).await;
564        };
565        let inner = self.0.spawn_boxed(Box::pin(fut));
566        AnyExecutorTask::new(inner, receiver)
567    }
568}
569
570trait AnyExecutorImpl: Send + Sync + Any {
571    fn spawn_boxed(
572        &self,
573        fut: Pin<Box<dyn Future<Output = ()> + Send>>,
574    ) -> Pin<Box<dyn Task<()> + Send>>;
575    fn as_any(&self) -> &dyn Any;
576}
577
578impl<T: Task<T>> Task<T> for Pin<Box<T>> {
579    fn poll_result(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<T, Error>> {
580        let this = unsafe { self.get_unchecked_mut() };
581        this.as_mut().poll_result(cx)
582    }
583}
584
585impl<E> AnyExecutorImpl for E
586where
587    E: Executor + 'static,
588{
589    fn spawn_boxed(
590        &self,
591        fut: Pin<Box<dyn Future<Output = ()> + Send>>,
592    ) -> Pin<Box<dyn Task<()> + Send>> {
593        let task = self.spawn(fut);
594        Box::pin(task)
595    }
596
597    fn as_any(&self) -> &dyn Any {
598        self
599    }
600}
601
602#[cfg(feature = "std")]
603mod std_on {
604    use alloc::boxed::Box;
605
606    use crate::{
607        AnyExecutor, AnyExecutorTask, AnyLocalExecutor, AnyLocalExecutorTask, Executor,
608        LocalExecutor,
609    };
610
611    extern crate std;
612
613    use core::{cell::OnceCell, future::Future, panic::AssertUnwindSafe};
614    use std::sync::OnceLock;
615    std::thread_local! {
616        static LOCAL_EXECUTOR: OnceCell<AnyLocalExecutor> = const { OnceCell::new() };
617    }
618
619    /// Initialize the thread-local executor for spawning non-Send futures.
620    ///
621    /// This must be called before using [`spawn_local`]. The executor will be used
622    /// for all [`spawn_local`] calls on the current thread.
623    ///
624    /// # Panics
625    ///
626    /// Panics if a local executor has already been set for this thread.
627    ///
628    pub fn init_local_executor(executor: impl LocalExecutor + 'static) {
629        if try_init_local_executor(executor).is_err() {
630            panic!("Local executor already set for this thread");
631        }
632    }
633
634    /// Try to initialize the thread-local executor for spawning non-Send futures.
635    ///
636    /// This is a non-panicking version of [`init_local_executor`].
637    pub fn try_init_local_executor<E>(executor: E) -> Result<(), E>
638    where
639        E: LocalExecutor + 'static,
640    {
641        LOCAL_EXECUTOR.with(|cell| {
642            cell.set(AnyLocalExecutor::new(executor))
643                .map_err(|e| *e.downcast().unwrap())
644        })
645    }
646
647    static GLOBAL_EXECUTOR: OnceLock<AnyExecutor> = OnceLock::new();
648
649    /// Initialize the global executor for spawning Send futures.
650    ///
651    /// This must be called before using [`spawn`]. The executor will be used
652    /// for all [`spawn`] calls across all threads.
653    ///
654    /// # Panics
655    ///
656    /// Panics if a global executor has already been set.
657    ///
658    pub fn init_global_executor(executor: impl crate::Executor + 'static) {
659        if GLOBAL_EXECUTOR.set(AnyExecutor::new(executor)).is_err() {
660            panic!("Global executor already set");
661        }
662    }
663
664    /// Try to initialize the global executor for spawning Send futures.
665    ///
666    /// This is a non-panicking version of [`init_global_executor`].
667    pub fn try_init_global_executor<E>(executor: E) -> Result<(), E>
668    where
669        E: crate::Executor + 'static,
670    {
671        GLOBAL_EXECUTOR
672            .set(AnyExecutor::new(executor))
673            .map_err(|e| *e.downcast().unwrap())
674    }
675
676    /// Spawn a `Send` future on the global executor.
677    ///
678    /// The global executor must be initialized with [`init_global_executor`] before
679    /// calling this function.
680    ///
681    /// # Panics
682    ///
683    /// Panics if the global executor has not been set.
684    ///
685    pub fn spawn<Fut>(fut: Fut) -> AnyExecutorTask<Fut::Output>
686    where
687        Fut: Future<Output: Send> + Send + 'static,
688    {
689        let executor = GLOBAL_EXECUTOR.get().expect("Global executor not set");
690        executor.spawn(fut)
691    }
692
693    /// Spawn a future on the thread-local executor.
694    ///
695    /// The local executor must be initialized with [`init_local_executor`] before
696    /// calling this function. Unlike [`spawn`], this can handle futures that are
697    /// not `Send`.
698    ///
699    /// # Panics
700    ///
701    /// Panics if the local executor has not been set for this thread.
702    ///
703    pub fn spawn_local<Fut>(fut: Fut) -> AnyLocalExecutorTask<Fut::Output>
704    where
705        Fut: Future + 'static,
706    {
707        LOCAL_EXECUTOR.with(|cell| {
708            let executor = cell.get().expect("Local executor not set");
709            executor.spawn_local(fut)
710        })
711    }
712
713    #[allow(unused)]
714    pub(crate) fn catch_unwind<F, R>(f: F) -> Result<R, Box<dyn std::any::Any + Send>>
715    where
716        F: FnOnce() -> R,
717    {
718        std::panic::catch_unwind(AssertUnwindSafe(f))
719    }
720
721    /// A default executor that uses the global and local executors.
722    ///
723    /// This executor delegates to the global executor for `Send` futures
724    /// and the local executor for non-`Send` futures.
725    #[derive(Clone, Copy, Debug)]
726    pub struct DefaultExecutor;
727
728    impl Executor for DefaultExecutor {
729        type Task<T: Send + 'static> = AnyExecutorTask<T>;
730
731        fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
732        where
733            Fut: core::future::Future<Output: Send> + Send + 'static,
734        {
735            spawn(fut)
736        }
737    }
738
739    impl LocalExecutor for DefaultExecutor {
740        type Task<T: 'static> = AnyLocalExecutorTask<T>;
741
742        fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
743        where
744            Fut: core::future::Future + 'static,
745        {
746            spawn_local(fut)
747        }
748    }
749}
750
751#[cfg(feature = "std")]
752pub use std_on::*;