Skip to main content

zeph_common/
spawner.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Dependency-inversion trait for supervised blocking thread spawns.
5//!
6//! Crates that cannot depend on `zeph-core` (e.g. `zeph-index`) accept an
7//! `Option<Arc<dyn BlockingSpawner>>` and fall back to raw
8//! `tokio::task::spawn_blocking` when `None`. This breaks the cyclic
9//! dependency that would otherwise arise from `zeph-index` importing
10//! `TaskSupervisor` from `zeph-core`.
11
12/// Trait for spawning CPU-bound work on a supervised blocking thread pool.
13///
14/// Implementors register each spawned task in their supervision layer so it
15/// is visible to lifecycle management (snapshots, graceful shutdown, metrics).
16/// Callers that do not have a supervised spawner may fall back to
17/// `tokio::task::spawn_blocking` directly.
18///
19/// The trait is object-safe: it accepts a `Box<dyn FnOnce() + Send + 'static>`
20/// and returns a `JoinHandle<()>`. Callers that need a typed return value
21/// must communicate results through a channel or shared state.
22///
23/// # Examples
24///
25/// ```no_run
26/// use std::sync::Arc;
27/// use zeph_common::BlockingSpawner;
28///
29/// fn do_work(spawner: Arc<dyn BlockingSpawner>) {
30///     let handle = spawner.spawn_blocking_named(Arc::from("my_task"), Box::new(|| {
31///         // CPU-bound work
32///     }));
33///     // Caller can `.await` the handle.
34///     let _ = handle;
35/// }
36/// ```
37pub trait BlockingSpawner: Send + Sync + 'static {
38    /// Spawn a named blocking closure and return a `JoinHandle<()>` for completion.
39    ///
40    /// Pass an `Arc<str>` for the task name — this avoids the need to leak memory
41    /// when constructing dynamic task names. Static literals can be converted with
42    /// `Arc::from("my_task")`.
43    ///
44    /// The implementation registers the task in its supervision layer before
45    /// the closure begins executing. Results must be communicated via channels
46    /// or shared state if needed.
47    ///
48    /// If the closure panics, the implementation should log the error rather than
49    /// propagating a panic to the caller. The returned `JoinHandle<()>` resolves
50    /// to `Ok(())` in all non-abort cases; it resolves to `Err(JoinError)` only
51    /// if the bridge task itself is aborted externally.
52    #[must_use]
53    fn spawn_blocking_named(
54        &self,
55        name: std::sync::Arc<str>,
56        f: Box<dyn FnOnce() + Send + 'static>,
57    ) -> tokio::task::JoinHandle<()>;
58}