some_executor 0.7.2

A trait for libraries that abstract over any executor
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Executors for futures that are `'static` but not `Send`.
//!
//! The static family is the third of the crate's three scopes. [`SomeExecutor`]
//! takes `Send + 'static` futures and can move them between threads;
//! [`SomeLocalExecutor`] takes futures that borrow from the caller's stack and
//! never leave it. These take futures that own everything they touch but are
//! still pinned to one thread — the shape a browser main thread or any
//! single-threaded runtime actually has.
//!
//! Split out of `lib.rs` because it is a self-contained family: three traits
//! and the `Infallible` implementation that lets the type be used wherever an
//! executor is named but none can exist.

use crate::*;

/**
A trait for executors that can spawn static, non-Send tasks.

This trait is designed for executors that can handle futures with a `'static` lifetime
but without the `Send` bound. This is useful for cases where you need static data
without the overhead of Send synchronization.

Unlike `SomeExecutor` which requires `Send` and `SomeLocalExecutor` which supports
arbitrary lifetimes, `SomeStaticExecutor` specifically targets the middle ground
of static lifetime without Send requirements.

# Use Cases

- Thread-local static data access
- Executors that work with static futures but don't need Send
- Applications with static lifetimes but thread-local execution
- Bridge between local and global executor patterns
*/
pub trait SomeStaticExecutor: 'static + Debug {
    /// The notifier handle that can wake or signal work for this executor.
    type ExecutorNotifier: ExecutorNotified;

    /**
    Spawns a static, non-Send future onto the runtime.

    # Parameters
    - `task`: The task to spawn containing a 'static future

    # Note

    The future must be `'static` but does not need to be `Send`. This allows
    for static data access without the synchronization overhead of Send.

    # Implementation notes

    For details on why F::Output is Unpin, see the comment on [observer::TypedObserver].
    */
    fn spawn_static<F, Notifier: ObserverNotified<F::Output>>(
        &mut self,
        task: Task<F, Notifier>,
    ) -> impl Observer<Value = F::Output>
    where
        Self: Sized,
        F: Future + 'static,
        F::Output: 'static + Unpin;

    /**
    Spawns a static, non-Send future onto the runtime.

    Like [Self::spawn_static], but some implementors may have a fast path for the async context.

    # Implementation notes

    For details on why F::Output is Unpin, see the comment on [observer::TypedObserver].
    */
    fn spawn_static_async<F, Notifier: ObserverNotified<F::Output>>(
        &mut self,
        task: Task<F, Notifier>,
    ) -> impl Future<Output = impl Observer<Value = F::Output>>
    where
        Self: Sized,
        F: Future + 'static,
        F::Output: 'static + Unpin;

    /**
    Spawns a static, non-Send future onto the runtime.

    # Note

    This differs from [Self::spawn_static] in that we take a boxed future, since we can't have generic fn.
    Implementations probably pin this with [Box::into_pin].
    */
    fn spawn_static_objsafe(&mut self, task: ObjSafeStaticTask) -> BoxedStaticObserver;

    /**
    Spawns a static, non-Send future onto the runtime.

    # Note

    This differs from [Self::spawn_static] in that we take a boxed future, since we can't have generic fn.
    Implementations probably pin this with [Box::into_pin].
    */
    fn spawn_static_objsafe_async<'s>(
        &'s mut self,
        task: ObjSafeStaticTask,
    ) -> BoxedStaticObserverFuture<'s>;

    /**
    Clones the executor.

    The returned value will spawn tasks onto the same executor.
    */
    fn clone_box(&self) -> Box<DynStaticExecutor>;

    /**
    Produces an executor notifier.
    */
    fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>;
}

/// Opt-in synchronous entry point for an executor that drives static, non-[`Send`]
/// tasks on the calling thread.
///
/// This trait has no default implementation. A current-thread executor must keep
/// running its scheduler while it waits for `future`; inheriting the parking behavior
/// of [`SomeExecutor::block_on`] would stop that scheduler and deadlock. Backends where
/// blocking is impossible should simply not implement this trait.
pub trait StaticBlockOn: SomeStaticExecutor {
    /// Drives `future` and this executor's scheduler until the future resolves.
    ///
    /// Neither the future nor its output needs to be [`Send`], and the future may
    /// borrow from the caller's stack.
    fn block_on_static<F: Future>(&mut self, future: F) -> F::Output
    where
        F::Output: 'static;
}

/// A non-objsafe descendant of [SomeStaticExecutor].
///
/// This trait provides a more ergonomic interface for static executors, but is not object-safe
/// due to the Clone requirement. Static executors handle futures with 'static lifetime
/// but without the Send requirement.
///
/// # Example
///
/// ```
/// # use some_executor::{StaticExecutorExt, task::Task};
/// # use std::rc::Rc;
/// # use std::convert::Infallible;
/// # fn example<E: StaticExecutorExt>(mut exec: E) {
/// // Can use 'static but !Send types
/// let task = Task::<_, Infallible>::without_notifications(
///     "example".to_string(),
///     Default::default(),
///     async { 42 }
/// );
/// let observer = exec.spawn_static(task);
/// # }
/// ```
pub trait StaticExecutorExt: SomeStaticExecutor + Clone {}

impl SomeStaticExecutor for Infallible {
    type ExecutorNotifier = Infallible;

    fn spawn_static<F, Notifier: ObserverNotified<F::Output>>(
        &mut self,
        _task: Task<F, Notifier>,
    ) -> impl Observer<Value = F::Output>
    where
        Self: Sized,
        F: Future + 'static,
        F::Output: 'static + Unpin,
    {
        #[allow(unreachable_code)]
        {
            unimplemented!() as TypedObserver<F::Output, Infallible>
        }
    }

    fn spawn_static_async<F, Notifier: ObserverNotified<F::Output>>(
        &mut self,
        _task: Task<F, Notifier>,
    ) -> impl Future<Output = impl Observer<Value = F::Output>>
    where
        Self: Sized,
        F: Future + 'static,
        F::Output: 'static + Unpin,
    {
        #[allow(unreachable_code)]
        #[allow(clippy::async_yields_async)]
        {
            async { todo!() as TypedObserver<F::Output, Infallible> }
        }
    }

    fn spawn_static_objsafe(&mut self, _task: ObjSafeStaticTask) -> BoxedStaticObserver {
        unimplemented!()
    }

    fn spawn_static_objsafe_async<'s>(
        &'s mut self,
        _task: ObjSafeStaticTask,
    ) -> BoxedStaticObserverFuture<'s> {
        unimplemented!()
    }

    fn clone_box(&self) -> Box<DynStaticExecutor> {
        unimplemented!()
    }

    fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier> {
        unimplemented!()
    }
}