processmanager 0.5.0

manage process lifecycles, graceful shutdown and process faults
Documentation

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

  1. Features
  2. Installation
  3. Quick-Start
  4. Built-in Helpers
  5. Examples
  6. High-Level Architecture
  7. Crate Features
  8. 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&nbsp;Handle) -- shutdown / reload --> Mgr
  • Every Runnable gets its own Tokio task.
  • A ProcessControlHandler allows 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.