Skip to main content

SomeLocalExecutor

Trait SomeLocalExecutor 

Source
pub trait SomeLocalExecutor<'future>: Debug {
    type ExecutorNotifier: ExecutorNotified;

    // Required methods
    fn spawn_local<F, Notifier: ObserverNotified<F::Output>>(
        &mut self,
        task: Task<F, Notifier>,
    ) -> impl Observer<Value = F::Output>
       where Self: Sized,
             F: Future + 'future,
             <F as Future>::Output: Unpin + 'static;
    fn spawn_local_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 + 'future,
             <F as Future>::Output: 'static + Unpin;
    fn spawn_local_objsafe(
        &mut self,
        task: ObjSafeLocalTask,
    ) -> BoxedLocalObserver;
    fn spawn_local_objsafe_async<'s>(
        &'s mut self,
        task: ObjSafeLocalTask,
    ) -> BoxedLocalObserverFuture<'s>;
    fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>;
}
Expand description

A trait for executors that can spawn tasks onto the local thread.

This type can spawn futures that are !Send.

§About the lifetime parameter

The lifetime parameter defines the lifetime of the executor itself, which is really the longest lifetime of any future it may be executing.

To understand this, it is helpful to consider 3 cases.

§Multithreaded executors

Multithreaded executors along the lines of SomeExecutor generally require their futures to be 'static. This is vaguely intuitive in the “it is nice to be able to move the future to another thread” sense, but in full detail it is less intuitive than it seems. If a future refers to data on the local stack frame, then the future may dangle if:

  1. The user returns upstack before the future completes, this could maybe be resolved with clever lifetimes?
  2. The thread panics before the future completes, this is a hard problem.
  3. The “thread” is really an async context, which is cancelled before the future completes, this is a hard problem.
  4. The thread is terminated by the OS for some reason, this is a hard problem.

For at least reasons 2-4, SomeExecutor implicitly requires 'static futures.

§Local executors, globally-scoped

Now let us consider a local executor with global scope (such as a main thread executor). These executors disptach onto the local thread but exist for a long time, such as the lifetime of the program. These types of executors have requirements not so dissimilar from multithreaded executors:

  1. The user returns upstack before the future completes. This is probably fine, if we poison the executor in some way (includingstatically), although care must be taken to ensure that the future’s memory does not escape. For example, in a DMA-type operation where the OS is writing to a buffer independently, that buffer must not be located on the stack.
  2. If the thread panics before the future completes, that’s probably fine as well since the executor is inherently poisoned by the panic. See the DMA-style caveat above.
  3. If the async context is cancelled before the future completes, that’s a big problem. It is a hard one to solve because it’s not obvious how to poison the executor deterministically.
  4. If the thread is terminated by the OS, the executor is poisoned so that’s ok.

Due to reason 3, local executors with global scope generally require 'static futures.

§Local executors, locally scoped

Alternatively we may spin up an executor e.g. on a stack frame, for a specific task. In that case:

  1. Returning upstack inherently poisons the executor, with sensible lifetime design/analysis.
  2. Panicking poisons the executor.
  3. Cancellation posions the executor since the executor is on the same stackframe
  4. The thread is terminated by the OS, the executor is poisoned.

In this case, the executor can support non-'static futures.

§Overall

In summary, for executors with global scope, `‘static’ should be chosen as the lifetime parameter.

For executors with local scope, this trait can be implemented for any lifetime.

When in doubt, the 'static lifetime can be chosen and upgraded later.

Required Associated Types§

Source

type ExecutorNotifier: ExecutorNotified

The notifier handle that can wake or signal work for this executor.

Required Methods§

Source

fn spawn_local<F, Notifier: ObserverNotified<F::Output>>( &mut self, task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output>
where Self: Sized, F: Future + 'future, <F as Future>::Output: Unpin + 'static,

Spawns a future onto the runtime.

§Parameters
  • task: The task to spawn.
Source

fn spawn_local_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 + 'future, <F as Future>::Output: 'static + Unpin,

Spawns a future onto the runtime.

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

Source

fn spawn_local_objsafe(&mut self, task: ObjSafeLocalTask) -> BoxedLocalObserver

Spawns a future onto the runtime.

§Note

This differs from SomeExecutor::spawn in that we take a boxed future, since we can’t have generic fn. Implementations probably pin this with Box::into_pin.

Source

fn spawn_local_objsafe_async<'s>( &'s mut self, task: ObjSafeLocalTask, ) -> BoxedLocalObserverFuture<'s>

Object-safe async variant of Self::spawn_local_objsafe.

Source

fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>

Returns an executor notifier when notification is supported.

Implementations on Foreign Types§

Source§

impl<'future> SomeLocalExecutor<'future> for Infallible

Source§

type ExecutorNotifier = Infallible

Source§

fn spawn_local<F, Notifier: ObserverNotified<F::Output>>( &mut self, _task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output>
where Self: Sized, F: Future + 'future, <F as Future>::Output: Unpin + 'static,

Source§

fn spawn_local_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 + 'future, <F as Future>::Output: 'static,

Source§

fn spawn_local_objsafe( &mut self, _task: Task<Pin<Box<dyn Future<Output = Box<dyn Any>>>>, Box<dyn ObserverNotified<dyn Any + 'static>>>, ) -> Box<dyn Observer<Value = Box<dyn Any>, Output = FinishedObservation<Box<dyn Any>>>>

Source§

fn spawn_local_objsafe_async<'s>( &'s mut self, _task: Task<Pin<Box<dyn Future<Output = Box<dyn Any>>>>, Box<dyn ObserverNotified<dyn Any + 'static>>>, ) -> Box<dyn Future<Output = Box<dyn Observer<Value = Box<dyn Any>, Output = FinishedObservation<Box<dyn Any>>>>> + 's>

Source§

fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>

Implementors§