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