| Crates.io | processmanager |
| lib.rs | processmanager |
| version | 0.5.0 |
| created_at | 2024-03-27 22:19:41.373239+00 |
| updated_at | 2025-07-08 21:05:25.624193+00 |
| description | manage process lifecycles, graceful shutdown and process faults |
| homepage | |
| repository | https://github.com/mrcrgl/processmanager-rs |
| max_upload_size | |
| id | 1188481 |
| size | 79,919 |
ProcessManager is a light-weight Tokio based supervisor for coordinating
multiple long-running asynchronous tasks (called processes).
It takes care of
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.
| 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. |
Add the dependency to your Cargo.toml:
[dependencies]
processmanager = "0"
Optional features are listed below.
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;
}
| 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"] }
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.
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
Runnable gets its own Tokio task.ProcessControlHandler allows external code to shut down or reload
a single process or the whole subtree.Err(_) terminates the entire supervisor.| 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.
Licensed under either of
at your option.