10 unstable releases (4 breaking)
Uses new Rust 2024
| 0.5.0 | Jul 8, 2025 |
|---|---|
| 0.4.1 | Jul 2, 2025 |
| 0.3.2 | Nov 10, 2024 |
| 0.3.0 | Sep 16, 2024 |
| 0.1.0 | Mar 27, 2024 |
#398 in Rust patterns
40 downloads per month
46KB
711 lines
ProcessManager
ProcessManager is a light-weight Tokio based supervisor for coordinating
multiple long-running asynchronous tasks (called processes).
It takes care of
- spawning all registered processes,
- forwarding reload / shutdown commands,
- propagating errors,
- initiating a graceful shutdown of the whole tree once any process fails,
- and optionally cleaning up completed children to avoid memory leaks.
The crate is completely runtime-agnostic except for its dependency on Tokio tasks behind the scenes; you are free to use any async code in your own processes.
Table of Contents
- Features
- Installation
- Quick-Start
- Built-in Helpers
- Examples
- High-Level Architecture
- Crate Features
- License
Features
| Capability | Description |
|---|---|
| Graceful shutdown | Propagates a single shutdown request to all children. |
| Dynamic child management | Add new Runnables even while the manager is already running. |
| Error propagation | A failing child triggers a global shutdown and returns the first error. |
| Auto cleanup | Optionally remove finished children to keep memory usage bounded. |
| Hierarchical composition | Managers implement Runnable themselves → build arbitrary process trees. |
| Built-in helpers | See IdleProcess and SignalReceiver. |
Installation
Add the dependency to your Cargo.toml:
[dependencies]
processmanager = "0"
Optional features are listed below.
Quick-Start
A minimal program that runs two workers for three seconds and then shuts down gracefully:
use processmanager::*;
use std::{sync::Arc, time::Duration};
use tokio::time::{interval, sleep};
struct Worker {
id: usize,
guard: Arc<RuntimeGuard>,
}
impl Worker {
fn new(id: usize) -> Self {
Self { id, guard: Arc::new(RuntimeGuard::default()) }
}
}
impl Runnable for Worker {
fn process_start(&self) -> ProcFuture<'_> {
let id = self.id;
let guard = self.guard.clone();
Box::pin(async move {
let ticker = guard.runtime_ticker().await;
let mut beat = interval(Duration::from_secs(1));
loop {
match ticker.tick(beat.tick()).await {
ProcessOperation::Next(_) => println!("worker-{id}: heartbeat"),
ProcessOperation::Control(RuntimeControlMessage::Shutdown) => break,
_ => continue,
}
}
Ok(())
})
}
fn process_handle(&self) -> Arc<dyn ProcessControlHandler> {
self.guard.handle()
}
}
#[tokio::main]
async fn main() {
let manager = ProcessManagerBuilder::default()
.pre_insert(Worker::new(0))
.pre_insert(Worker::new(1))
.build();
let handle = manager.process_handle();
tokio::spawn(async move {
manager.process_start().await.expect("manager error");
});
sleep(Duration::from_secs(3)).await;
handle.shutdown().await;
}
Built-in Helpers
| Helper | Purpose | Feature flag |
|---|---|---|
IdleProcess |
Keeps an otherwise empty manager alive until an external shutdown is requested. | — |
SignalReceiver |
Listens for SIGHUP, SIGINT, SIGTERM, SIGQUIT and converts them into shutdown / reload control messages. |
signal |
Enable SignalReceiver like this:
processmanager = { version = "0", features = ["signal"] }
Examples
Ready-to-run examples live in examples/ and can be launched with
Cargo:
| Command | Highlights |
|---|---|
cargo run --example simple |
Minimal setup, two workers, graceful shutdown |
cargo run --example dynamic_add |
Dynamically add workers while the manager is running |
Feel free to copy or adapt the code for your own services.
High-Level Architecture
flowchart TD
subgraph Supervisor
Mgr(ProcessManager)
end
Mgr -->|spawn| Child1[Runnable #1]
Mgr --> Child2[Runnable #2]
Child1 -- control --> Mgr
Child2 -- control --> Mgr
ExtHandle(External Handle) -- shutdown / reload --> Mgr
- Every
Runnablegets its own Tokio task. - A
ProcessControlHandlerallows external code to shut down or reload a single process or the whole subtree. - The first child that ends in
Err(_)terminates the entire supervisor.
Crate Features
| Feature | Default? | Description |
|---|---|---|
signal |
no | Activates builtin::SignalReceiver for Unix signal handling. |
tracing |
no | Emit structured log events via the tracing crate. |
log |
no | Use the log crate for textual logging if tracing is disabled. |
Pick one of tracing or log to avoid duplicate output.
License
Licensed under either of
- Apache License, Version 2.0
- MIT license
at your option.
Dependencies
~3–16MB
~127K SLoC