pub trait SomeExecutor:
Send
+ Sync
+ Debug {
type ExecutorNotifier: ExecutorNotified + Send;
// Required methods
fn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&mut self,
task: Task<F, Notifier>,
) -> impl Observer<Value = F::Output> + Send
where Self: Sized,
F::Output: Send + Unpin;
fn spawn_async<'s, F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&'s mut self,
task: Task<F, Notifier>,
) -> impl Future<Output = impl Observer<Value = F::Output>> + Send + 's
where Self: Sized,
F::Output: Send + Unpin;
fn spawn_objsafe(&mut self, task: ObjSafeTask) -> BoxedSendObserver;
fn spawn_objsafe_async<'s>(
&'s mut self,
task: ObjSafeTask,
) -> BoxedSendObserverFuture<'s>;
fn clone_box(&self) -> Box<DynExecutor>;
fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>;
// Provided methods
fn block_on<F: Future>(&mut self, future: F) -> F::Output
where Self: Sized,
F::Output: Send + 'static { ... }
fn block_on_objsafe(
&mut self,
future: BoxedBlockOnFuture<'_>,
) -> Box<dyn Any + Send> { ... }
}Expand description
A trait targeting ‘some’ executor.
Code targeting this trait can spawn tasks on an executor without knowing which executor it is. This is the core abstraction that allows writing executor-agnostic async code.
If possible, use the SomeExecutorExt trait instead for a more ergonomic API.
This trait is primarily useful when you need an object-safe trait for dynamic dispatch.
§Example
let future = Box::new(async { Box::new(42) as Box<dyn Any + Send> });
let task = Task::new_objsafe(
"example".to_string(),
future,
Default::default(),
None
);
let observer = exec.spawn_objsafe(task);
// Can track task completion via observerRequired Associated Types§
Sourcetype ExecutorNotifier: ExecutorNotified + Send
type ExecutorNotifier: ExecutorNotified + Send
§Design notes
I think we want ExecutorNotified to imply Send (or at least in the case of SomeExecutor trait). We want to permit the notifier to be sent into e.g. the future, running on a threadpool for example.
Required Methods§
Sourcefn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&mut self,
task: Task<F, Notifier>,
) -> impl Observer<Value = F::Output> + Send
fn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>( &mut self, task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output> + Send
Spawns a future onto the runtime.
§Parameters
task: The task to spawn.
§Note
Send and 'static are generally required to move the future onto a new thread.
§Implementation notes
Implementations should generally ensure that a dlog-context is available to the future.
For details on why F::Output is Unpin, see the comment on observer::TypedObserver.
Sourcefn spawn_async<'s, F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&'s mut self,
task: Task<F, Notifier>,
) -> impl Future<Output = impl Observer<Value = F::Output>> + Send + 's
fn spawn_async<'s, F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>( &'s mut self, task: Task<F, Notifier>, ) -> impl Future<Output = impl Observer<Value = F::Output>> + Send + 's
Spawns a future onto the runtime.
Like Self::spawn, 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.
Sourcefn spawn_objsafe(&mut self, task: ObjSafeTask) -> BoxedSendObserver
fn spawn_objsafe(&mut self, task: ObjSafeTask) -> BoxedSendObserver
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.
Sourcefn spawn_objsafe_async<'s>(
&'s mut self,
task: ObjSafeTask,
) -> BoxedSendObserverFuture<'s>
fn spawn_objsafe_async<'s>( &'s mut self, task: ObjSafeTask, ) -> BoxedSendObserverFuture<'s>
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.
Sourcefn clone_box(&self) -> Box<DynExecutor>
fn clone_box(&self) -> Box<DynExecutor>
Clones the executor.
The returned value will spawn tasks onto the same executor.
Sourcefn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>
fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>
Produces an executor notifier.
Provided Methods§
Sourcefn block_on<F: Future>(&mut self, future: F) -> F::Output
fn block_on<F: Future>(&mut self, future: F) -> F::Output
Drives future to completion on this executor, blocking the calling thread until it
resolves.
This is the one-way door from sync into async: fn main, a test, a CLI tool, or an
FFI callback crosses here once and is executor-agnostic from then on. For the
duration of the block, this executor is installed as the thread’s executor, so
current_executor inside the future finds it.
Unlike spawning, the future is polled in place and so needs neither Send nor
'static; it may borrow from the caller’s stack. F::Output must be Send and
'static because the value is erased through Self::block_on_objsafe, which is
what lets Box<DynExecutor> — and therefore
current_executor and
global_executor — dispatch to the real
executor rather than to the default below. If you need a value that is not Send,
call the free block_on function instead and forgo executor dispatch.
§Implementation notes
Customize Self::block_on_objsafe, not this method. This one only erases the
output type, calls that one, and downcasts the result back — so overriding that one
changes both paths, while overriding this one leaves anyone holding a
Box<DynExecutor> on the default.
The default parks the calling thread between polls and polls the future there. Two
independent things have to be true for that to be correct, and a backend must
override Self::block_on_objsafe if either fails:
- Something else makes progress while this thread is parked. An executor that
runs tasks on the calling thread — a current-thread or local executor — must run
its own polling loop until the future resolves instead, because parking the
caller stops the loop the future is waiting on. This one the default detects and
reports rather than hanging silently; see the
block_onmodule documentation. - The polling thread is in whatever ambient context your resources need. The
default installs this executor as the thread executor and nothing else. A backend
whose timers, sockets or channels resolve their driver through a thread-local of
its own — tokio, via its runtime
EnterGuard— must override even though it satisfies (1), because its workers running elsewhere does not put this thread in the runtime. Otherwise the first context-dependent.awaitinside the future fails, and it fails immediately rather than stalling, so the detector above will not catch it.
An override that wants the default’s scoping can get it from
install_thread_executor, which restores
the previous thread executor — including none — when its guard drops.
§Panics
May panic if called from inside a task already running on this executor; re-entrancy is not supported and is a deadlock on most backends.
The default cannot observe a cancellation — it polls the future directly and no one else holds a handle to it. An override that spawns the future internally should panic if the task is cancelled out from under the caller, rather than inventing an output.
Sourcefn block_on_objsafe(
&mut self,
future: BoxedBlockOnFuture<'_>,
) -> Box<dyn Any + Send>
fn block_on_objsafe( &mut self, future: BoxedBlockOnFuture<'_>, ) -> Box<dyn Any + Send>
Drives a type-erased future to completion, blocking the calling thread.
§Note
This differs from Self::block_on in that we take a boxed future, since we can’t
have generic fn. This is the method that carries the behavior; the typed one is a
wrapper that erases and then downcasts.
See Self::block_on for the contract, and for why an executor that runs tasks on
the calling thread must override this.
Trait Implementations§
Source§impl<UnderlyingNotifier: ExecutorNotified + Send> SomeExecutor for Box<dyn SomeExecutor<ExecutorNotifier = UnderlyingNotifier>>
Implementation of SomeExecutor for boxed executor trait objects.
impl<UnderlyingNotifier: ExecutorNotified + Send> SomeExecutor for Box<dyn SomeExecutor<ExecutorNotifier = UnderlyingNotifier>>
Implementation of SomeExecutor for boxed executor trait objects.
This implementation enables Box<dyn SomeExecutor> to act as an executor itself,
providing the crucial type erasure pattern that allows executors to be stored and
used polymorphically.
§How It Works
When you spawn a task on a boxed executor:
- The concrete future type is converted to a type-erased version using
into_objsafe() - The type-erased task is spawned on the underlying executor
- A
DowncastObserveris returned that will convert results back to the original type
§Examples
let mut executor: Box<dyn SomeExecutor<ExecutorNotifier = Infallible>> = todo!();
// Spawn a typed future
let task = Task::without_notifications(
"hello-task".to_string(),
Configuration::default(),
async { "hello".to_string() }
);Source§fn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&mut self,
task: Task<F, Notifier>,
) -> impl Observer<Value = F::Output>
fn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>( &mut self, task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output>
Spawns a future onto the underlying executor with automatic type erasure and recovery.
This method handles the type erasure process transparently, converting the typed future into a type-erased version for the underlying executor, then wrapping the returned observer to restore type information.
Source§async fn spawn_async<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&mut self,
task: Task<F, Notifier>,
) -> impl Observer<Value = F::Output>
async fn spawn_async<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>( &mut self, task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output>
Asynchronously spawns a future onto the underlying executor.
Similar to spawn, but allows the spawning process itself to be asynchronous.
Source§fn spawn_objsafe(
&mut self,
task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>,
) -> Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>
fn spawn_objsafe( &mut self, task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>, ) -> Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>
Spawns an already type-erased task.
This is the object-safe method that works with type-erased futures directly. User code typically doesn’t call this; it’s used internally by the typed spawn methods.
Source§fn spawn_objsafe_async<'s>(
&'s mut self,
task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>,
) -> Box<dyn Future<Output = Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>> + 's>
fn spawn_objsafe_async<'s>( &'s mut self, task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>, ) -> Box<dyn Future<Output = Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>> + 's>
Asynchronously spawns an already type-erased task.
The async version of spawn_objsafe for executors that need async initialization.
Source§fn clone_box(&self) -> Box<DynExecutor>
fn clone_box(&self) -> Box<DynExecutor>
Creates a boxed clone of the executor.
This allows the boxed executor to be cloned while preserving the type erasure.
Source§fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>
fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>
Gets a notifier for executor-level events, if supported.
Returns a type-erased notifier that can be used to wake the executor.
Source§fn block_on_objsafe(
&mut self,
future: BoxedBlockOnFuture<'_>,
) -> Box<dyn Any + Send>
fn block_on_objsafe( &mut self, future: BoxedBlockOnFuture<'_>, ) -> Box<dyn Any + Send>
Blocks on the underlying executor rather than on the box.
Without this forwarding the box would take the trait’s default — parking this
thread — and an underlying current-thread executor that correctly overrode
block_on_objsafe would never be asked. Since the typed block_on delegates
here, forwarding this one method is enough to make both paths reach the real
executor.
Source§type ExecutorNotifier = Box<dyn ExecutorNotified + Send>
type ExecutorNotifier = Box<dyn ExecutorNotified + Send>
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementations on Foreign Types§
Source§impl<UnderlyingNotifier: ExecutorNotified + Send> SomeExecutor for Box<dyn SomeExecutor<ExecutorNotifier = UnderlyingNotifier>>
Implementation of SomeExecutor for boxed executor trait objects.
impl<UnderlyingNotifier: ExecutorNotified + Send> SomeExecutor for Box<dyn SomeExecutor<ExecutorNotifier = UnderlyingNotifier>>
Implementation of SomeExecutor for boxed executor trait objects.
This implementation enables Box<dyn SomeExecutor> to act as an executor itself,
providing the crucial type erasure pattern that allows executors to be stored and
used polymorphically.
§How It Works
When you spawn a task on a boxed executor:
- The concrete future type is converted to a type-erased version using
into_objsafe() - The type-erased task is spawned on the underlying executor
- A
DowncastObserveris returned that will convert results back to the original type
§Examples
let mut executor: Box<dyn SomeExecutor<ExecutorNotifier = Infallible>> = todo!();
// Spawn a typed future
let task = Task::without_notifications(
"hello-task".to_string(),
Configuration::default(),
async { "hello".to_string() }
);Source§fn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&mut self,
task: Task<F, Notifier>,
) -> impl Observer<Value = F::Output>
fn spawn<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>( &mut self, task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output>
Spawns a future onto the underlying executor with automatic type erasure and recovery.
This method handles the type erasure process transparently, converting the typed future into a type-erased version for the underlying executor, then wrapping the returned observer to restore type information.
Source§async fn spawn_async<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>(
&mut self,
task: Task<F, Notifier>,
) -> impl Observer<Value = F::Output>
async fn spawn_async<F: Future + Send + 'static, Notifier: ObserverNotified<F::Output> + Send>( &mut self, task: Task<F, Notifier>, ) -> impl Observer<Value = F::Output>
Asynchronously spawns a future onto the underlying executor.
Similar to spawn, but allows the spawning process itself to be asynchronous.
Source§fn spawn_objsafe(
&mut self,
task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>,
) -> Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>
fn spawn_objsafe( &mut self, task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>, ) -> Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>
Spawns an already type-erased task.
This is the object-safe method that works with type-erased futures directly. User code typically doesn’t call this; it’s used internally by the typed spawn methods.
Source§fn spawn_objsafe_async<'s>(
&'s mut self,
task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>,
) -> Box<dyn Future<Output = Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>> + 's>
fn spawn_objsafe_async<'s>( &'s mut self, task: Task<Pin<Box<dyn Future<Output = Box<dyn Any + Send + 'static>> + Send + 'static>>, Box<dyn ObserverNotified<dyn Any + Send> + Send>>, ) -> Box<dyn Future<Output = Box<dyn Observer<Value = Box<dyn Any + Send>, Output = FinishedObservation<Box<dyn Any + Send>>> + Send>> + 's>
Asynchronously spawns an already type-erased task.
The async version of spawn_objsafe for executors that need async initialization.
Source§fn clone_box(&self) -> Box<DynExecutor>
fn clone_box(&self) -> Box<DynExecutor>
Creates a boxed clone of the executor.
This allows the boxed executor to be cloned while preserving the type erasure.
Source§fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>
fn executor_notifier(&mut self) -> Option<Self::ExecutorNotifier>
Gets a notifier for executor-level events, if supported.
Returns a type-erased notifier that can be used to wake the executor.
Source§fn block_on_objsafe(
&mut self,
future: BoxedBlockOnFuture<'_>,
) -> Box<dyn Any + Send>
fn block_on_objsafe( &mut self, future: BoxedBlockOnFuture<'_>, ) -> Box<dyn Any + Send>
Blocks on the underlying executor rather than on the box.
Without this forwarding the box would take the trait’s default — parking this
thread — and an underlying current-thread executor that correctly overrode
block_on_objsafe would never be asked. Since the typed block_on delegates
here, forwarding this one method is enough to make both paths reach the real
executor.