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