pub struct TaskSupervisor { /* private fields */ }Expand description
Shared, cloneable handle to the supervised lifecycle task registry.
TaskSupervisor manages named, long-lived background tasks with restart
policies, health snapshots, and graceful shutdown. It is Clone + Send + Sync
so it can be distributed to bootstrap code, TUI, and shutdown orchestration
without any additional synchronisation.
§Thread safety
Interior state is guarded by a parking_lot::Mutex. The lock is never
held across .await points.
§Examples
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use zeph_common::task_supervisor::{RestartPolicy, TaskDescriptor, TaskSupervisor};
let cancel = CancellationToken::new();
let sup = TaskSupervisor::new(cancel.clone());
let _handle = sup.spawn(TaskDescriptor {
name: "watcher",
restart: RestartPolicy::RunOnce,
factory: || async { tokio::time::sleep(std::time::Duration::from_secs(1)).await },
});
sup.shutdown_all(Duration::from_secs(5)).await;Implementations§
Source§impl TaskSupervisor
impl TaskSupervisor
Sourcepub fn new(cancel: CancellationToken) -> Self
pub fn new(cancel: CancellationToken) -> Self
Create a new supervisor and start its reap driver.
The cancel token is propagated into every spawned task via tokio::select!.
When the token is cancelled, all tasks exit cooperatively on their next
cancellation check. Call shutdown_all to wait for
them to finish.
When called outside a Tokio runtime context (e.g. in synchronous unit tests), the reap driver is skipped. The supervisor still accepts task registrations but completion callbacks are not processed — safe because no tasks can actually be spawned without a runtime.
Sourcepub fn spawn<F, Fut>(&self, desc: TaskDescriptor<F>) -> TaskHandle
pub fn spawn<F, Fut>(&self, desc: TaskDescriptor<F>) -> TaskHandle
Spawn a named, supervised async task.
If a task with the same name already exists, it is aborted before the
new one is started.
§Examples
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use zeph_common::task_supervisor::{RestartPolicy, TaskDescriptor, TaskHandle, TaskSupervisor};
let cancel = CancellationToken::new();
let sup = TaskSupervisor::new(cancel.clone());
let handle: TaskHandle = sup.spawn(TaskDescriptor {
name: "config-watcher",
restart: RestartPolicy::Restart { max: 3, base_delay: Duration::from_secs(1) },
factory: || async { /* watch loop */ },
});Sourcepub fn spawn_classified<F, Fut, R>(
&self,
desc: TaskDescriptor<F>,
is_success: impl Fn(&R) -> bool + Send + Sync + 'static,
) -> TaskHandle
pub fn spawn_classified<F, Fut, R>( &self, desc: TaskDescriptor<F>, is_success: impl Fn(&R) -> bool + Send + Sync + 'static, ) -> TaskHandle
Spawn a named, supervised async task like spawn, but classify the
produced value’s own success/failure via is_success — mirrors
spawn_oneshot_classified except it supports
RestartPolicy::Restart and returns a TaskHandle (not a typed
BlockingHandle<R>), since a restart-capable task’s result value cannot be handed
back to a single caller across restarts — each restart attempt produces a fresh value
that only the registry’s TaskStatus can observe.
This matters whenever Fut::Output itself encodes failure (typically Result<T, E>):
without a classifier, a task that runs to completion but produces Err(..) is
classified and reported identically to one that produced Ok(..) — spawn alone only
observes whether the outer future resolved, never what value it resolved to.
is_success is called once, by reference, after the future resolves.
§Examples
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use zeph_common::task_supervisor::{RestartPolicy, TaskDescriptor, TaskSupervisor};
let sup = TaskSupervisor::new(CancellationToken::new());
let _handle = sup.spawn_classified(
TaskDescriptor {
name: "fallible-service",
restart: RestartPolicy::Restart { max: 0, base_delay: Duration::from_secs(1) },
factory: || async { Result::<(), String>::Err("boom".to_string()) },
},
Result::is_ok,
);Sourcepub fn spawn_blocking<F, R>(&self, name: Arc<str>, f: F) -> BlockingHandle<R>
pub fn spawn_blocking<F, R>(&self, name: Arc<str>, f: F) -> BlockingHandle<R>
Spawn a CPU-bound closure on the OS blocking thread pool.
The closure runs via tokio::task::spawn_blocking — it is never polled
on tokio worker threads and cannot block async I/O. The task is registered
in the supervisor registry and is visible to snapshot
and shutdown_all.
Dropping the returned BlockingHandle without calling .join() does
not cancel the task; it runs to completion but the result is discarded.
A panic inside f is captured and returned as BlockingError::Panicked
rather than propagating to the caller.
§Examples
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use zeph_common::task_supervisor::{BlockingHandle, TaskSupervisor};
let cancel = CancellationToken::new();
let sup = TaskSupervisor::new(cancel);
let handle: BlockingHandle<u32> = sup.spawn_blocking(Arc::from("compute"), || {
// CPU-bound work — safe to block here
42_u32
});
let result = handle.join().await.unwrap();
assert_eq!(result, 42);§Capacity limit
At most 8 spawn_blocking tasks run concurrently. Additional tasks wait for a
semaphore permit, bounding thread-pool growth under burst load.
§Panics
Panics inside f are captured and returned as BlockingError::Panicked — they
do not propagate to the caller.
Sourcepub fn spawn_oneshot<F, Fut, R>(
&self,
name: Arc<str>,
factory: F,
) -> BlockingHandle<R>
pub fn spawn_oneshot<F, Fut, R>( &self, name: Arc<str>, factory: F, ) -> BlockingHandle<R>
Spawn an async task that produces a typed result value (runs on tokio worker thread).
Unlike spawn, no restart policy is supported — the task
runs once. The task is registered in the supervisor registry under the
provided name and is visible to snapshot and
shutdown_all.
For CPU-bound work that must not block tokio workers, use
spawn_blocking instead.
§Examples
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use zeph_common::task_supervisor::{BlockingHandle, TaskSupervisor};
let cancel = CancellationToken::new();
let sup = TaskSupervisor::new(cancel.clone());
let handle: BlockingHandle<u32> = sup.spawn_oneshot(Arc::from("compute"), || async { 42_u32 });
let result = handle.join().await.unwrap();
assert_eq!(result, 42);Sourcepub fn spawn_oneshot_classified<F, Fut, R>(
&self,
name: Arc<str>,
factory: F,
is_success: impl Fn(&R) -> bool + Send + 'static,
) -> BlockingHandle<R>
pub fn spawn_oneshot_classified<F, Fut, R>( &self, name: Arc<str>, factory: F, is_success: impl Fn(&R) -> bool + Send + 'static, ) -> BlockingHandle<R>
Spawn an async task like spawn_oneshot, but additionally
classify the produced value’s own success/failure via is_success instead of only
the outer JoinHandle outcome (normal exit vs. panic vs. cancellation).
This matters whenever Fut::Output itself encodes failure (typically
Result<T, E>): without a classifier, a task that runs to completion but produces
Err(..) is classified and logged identically to one that produced Ok(..) —
spawn_oneshot alone only observes whether the outer future resolved, never what
value it resolved to. is_success is called once, by reference, before the value is
sent to the returned BlockingHandle’s receiver — it never consumes or blocks on
the value, and the value itself is delivered to the caller unchanged either way.
§Examples
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use zeph_common::task_supervisor::TaskSupervisor;
let sup = TaskSupervisor::new(CancellationToken::new());
let handle = sup.spawn_oneshot_classified(
Arc::from("fallible-task"),
|| async { Result::<u32, String>::Err("boom".to_string()) },
Result::is_ok,
);
let result = handle.join().await.unwrap();
assert!(result.is_err());Sourcepub fn abort(&self, name: &'static str)
pub fn abort(&self, name: &'static str)
Abort a task by name. No-op if no task with that name is registered.
Sourcepub async fn shutdown_all(&self, timeout: Duration)
pub async fn shutdown_all(&self, timeout: Duration)
Gracefully shut down all supervised tasks.
Cancels the supervisor’s CancellationToken and waits up to timeout
for all tasks to exit. Tasks that do not exit within the timeout are
aborted forcefully and their registry entries updated to TaskStatus::Aborted.
§Note
This cancels the token passed to TaskSupervisor::new. If you share
that token with other subsystems, they will be cancelled too. Use a child
token (cancel.child_token()) when the supervisor should not affect
unrelated components.
Sourcepub fn snapshot(&self) -> Vec<TaskSnapshot>
pub fn snapshot(&self) -> Vec<TaskSnapshot>
Return a point-in-time snapshot of all registered tasks.
Suitable for TUI status panels and structured logging. The returned
list is sorted by started_at ascending.
Sourcepub fn active_count(&self) -> usize
pub fn active_count(&self) -> usize
Return the number of tasks currently in Running or Restarting state.
Sourcepub fn cancellation_token(&self) -> CancellationToken
pub fn cancellation_token(&self) -> CancellationToken
Return a clone of the supervisor’s CancellationToken.
Callers can use this to check whether shutdown has been initiated.
Trait Implementations§
Source§impl BlockingSpawner for TaskSupervisor
impl BlockingSpawner for TaskSupervisor
Source§fn spawn_blocking_named(
&self,
name: Arc<str>,
f: Box<dyn FnOnce() + Send + 'static>,
) -> JoinHandle<()> ⓘ
fn spawn_blocking_named( &self, name: Arc<str>, f: Box<dyn FnOnce() + Send + 'static>, ) -> JoinHandle<()> ⓘ
Spawn a named blocking closure through the supervisor.
The task is registered in the supervisor registry (visible in
snapshot and subject to graceful shutdown) before
the closure begins executing.
Source§impl Clone for TaskSupervisor
impl Clone for TaskSupervisor
Source§fn clone(&self) -> TaskSupervisor
fn clone(&self) -> TaskSupervisor
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more