Skip to main content

teksilo_async/
install.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! `install_async()` — the builder hook that wires the executor into the app.
5
6use teksilo_app::TeksiloAppBuilder;
7
8use crate::executor::AsyncRuntimeHandle;
9
10/// Adds [`install_async`](TeksiloAppBuilderAsyncExt::install_async) to the app
11/// builder. Brought into scope with `use teksilo_async::TeksiloAppBuilderAsyncExt;`
12/// (or via the `teksilo` prelude when the `async` feature is on).
13pub trait TeksiloAppBuilderAsyncExt {
14    /// Install the main-thread async runtime: register the
15    /// [`AsyncRuntimeHandle`] and its completion registry in app-state, and
16    /// wire the executor poll into the event loop via
17    /// [`on_loop_tick`](TeksiloAppBuilder::on_loop_tick). After this, handlers
18    /// can call `ctx.spawn_local(...)` /
19    /// [`spawn_blocking`](crate::spawn_blocking).
20    fn install_async(self) -> Self;
21}
22
23impl TeksiloAppBuilderAsyncExt for TeksiloAppBuilder {
24    fn install_async(self) -> Self {
25        let handle = AsyncRuntimeHandle::new();
26        let poll_source = handle.poll_source();
27        // The completion registry is a teksilo-core type, so teksilo-app can
28        // fetch it from app-state and route `spawn_local_with` completions
29        // without depending on teksilo-async (which would be a cycle).
30        let completions = handle.completions();
31        let tick_handle = handle.clone();
32        let waker_handle = handle.clone();
33        self.app_state(handle)
34            .app_state(completions)
35            .on_loop_tick(poll_source, move || tick_handle.tick())
36            // Wire the cross-thread waker at startup (the AppEventProxy is itself
37            // an AppEventPoster), so a spawn wakes the loop even when the app
38            // uses the handle directly rather than via `ctx.spawn_local`. The
39            // ext trait also sets it lazily; `set_poster` is idempotent.
40            .on_ready(move |proxy| waker_handle.set_poster(std::sync::Arc::new(proxy)))
41    }
42}