Skip to main content

AsyncExecutor

Trait AsyncExecutor 

Source
pub trait AsyncExecutor: Send + Sync {
    // Required method
    fn block_on_dyn(
        &self,
        future: Pin<Box<dyn Future<Output = ()> + Send>>,
    ) -> Box<dyn Any + Send>;

    // Provided method
    fn block_on_for(&self) -> Option<&'static dyn AsyncExecutor> { ... }
}
Expand description

§Runtime-Agnostic Async Executor

Bridge for the #[tool] macro’s sync-from-async path that decouples Tokitai from Tokio. The macro’s sync wrapper delegates to a registered AsyncExecutor; install one with set_async_executor to use async-std, smol, embassy, or any custom executor.

§Default behaviour

  1. Custom executor registered via set_async_executor -> use it.
  2. Otherwise, if inside a Tokio runtime, use the active Handle::block_on (preserves backward compatibility).
  3. Otherwise, return a ToolError with a descriptive English message.

§No-std fallback

When the serde feature is disabled the crate is no_std and no executor is reachable; block_on_async always returns a ToolError::InternalError so the macro can still type-check.

This trait is object-safe so the user-registered executor can be stored as &'static dyn AsyncExecutor. The type-erased AsyncExecutor::block_on_dyn takes a boxed future and returns a boxed output; typed convenience is provided by AsyncExecutorExt.

§T-003 per-call override seam

Implementations may override AsyncExecutor::block_on_for to return Some(...) when an ambient executor (per-call_tool, per-thread, etc.) is reachable. The #[tool] macro’s sync-from-async wrapper probes block_on_for first, then the global slot set via set_async_executor, then the active Tokio runtime. This is how async-std / smol / embassy users supply an executor without mutating global state.

Required Methods§

Source

fn block_on_dyn( &self, future: Pin<Box<dyn Future<Output = ()> + Send>>, ) -> Box<dyn Any + Send>

Object-safe entry point: drive a boxed future to completion on the current thread and return its output as a boxed Any. Most users implement AsyncExecutorExt::block_on and let the blanket impl derive this; override it directly only for full control.

§Example
use tokitai_core::AsyncExecutor;
use core::future::Future;
use core::pin::Pin;
use core::any::Any;

struct ThreadPoolExecutor;
impl AsyncExecutor for ThreadPoolExecutor {
    fn block_on_dyn(
        &self,
        future: Pin<Box<dyn Future<Output = ()> + Send>>,
    ) -> Box<dyn Any + Send> {
        // ... drive the future to completion ...
    }
}

Provided Methods§

Source

fn block_on_for(&self) -> Option<&'static dyn AsyncExecutor>

Per-call override seam (T-003). The #[tool] macro’s sync wrapper probes this before the global slot, so users who do not want a process-wide side effect can supply an executor locally.

Return None to indicate “no ambient executor; fall through to the global slot / Tokio probe.” The default implementation always returns None; runtime-aware executors (async-std, smol, embassy) override it to return Some(...) when their runtime is reachable on the current thread.

§Example
use tokitai_core::AsyncExecutor;

struct AsyncStdExecutor;
impl AsyncExecutor for AsyncStdExecutor {
    fn block_on_dyn(
        &self,
        future: core::pin::Pin<
            Box<dyn core::future::Future<Output = ()> + Send>,
        >,
    ) -> Box<dyn core::any::Any + Send> {
        // ... drive the future ...
    }

    fn block_on_for(
        &self,
    ) -> Option<&'static dyn AsyncExecutor> {
        // async-std sets a thread-local handle; return `Some(self)`
        // when one is reachable on this thread.
        None
    }
}

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§