Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ProcessState

ProcessState is an explicit lifecycle state enum that replaces the binary is_running() -> bool check with a granular state machine.

Overview

Each managed process has a ProcessState that reflects its current lifecycle phase. The state is updated lazily by state() / is_running() via non-blocking try_wait(), and explicitly by stop(), restart(), and the reaper thread.

Variants

VariantDescription
StartingThe process is being spawned and has not yet been confirmed running. Transient state between spawn() and insertion into the manager.
RunningThe process is alive and running. Confirmed via try_wait() returning Ok(None).
WaitingThe process is queued but waiting for dependencies to become Running. No OS child process has been spawned yet. is_alive() returns true.
StoppingA stop signal has been sent and the manager is waiting for exit. Set by stop() / stop_many().
StoppedThe process has exited normally (exit code 0 or stopped by the manager within the grace period).
CrashedThe process exited unexpectedly with a non-zero exit code or signal.
RestartingA restart is in progress - the process is in backoff wait. The OS child handle is None (resources released). send_signal() returns an error; stop() cancels backoff silently; restart() spawns immediately.
FailedThe process failed to start, could not be killed, exhausted restarts (rate limit exceeded or spawn failure during automatic restart), or a dependency entered a terminal state.

State Transitions

graph TD
    classDef default fill: #1e1e1e, stroke: #333333, stroke-width: 1px, color: #ffffff
    classDef active fill: #89fc00, stroke: #333333, stroke-width: 2px, color: #000
    classDef transient fill: #f5b700, stroke: #333333, stroke-width: 2px, color: #000000
    classDef terminal fill: #dc0073, stroke: #333333, stroke-width: 2px, color: #ffffff

    Starting --> Running
    Waiting --> Starting
    Running --> Stopping
    Stopping --> Stopped
    Running --> Crashed
    Stopping --> Crashed
    Crashed --> Restarting
    Stopped --> Restarting
    Restarting --> Starting
    Restarting --> Waiting
    Running --> Failed
    Restarting --> Failed
    Waiting --> Failed
    Starting --> Failed

    class Starting,Running,Stopping,Restarting,Waiting active
    class Restarting transient
    class Stopped,Crashed,Failed terminal

Helper Methods

MethodReturnsDescription
is_alive()booltrue for Starting, Running, Waiting, Stopping, Restarting - equivalent to the old is_running() semantics
is_terminated()booltrue for Stopped, Crashed, Failed

Trait Implementations

  • Debug, Clone, Copy, PartialEq, Eq, Hash
  • Default - defaults to Starting
  • Display - lowercase string ("starting", "running", etc.)

Usage

#![allow(unused)]
fn main() {
use process_manager::{ProcessConfig, ProcessManager, ProcessState, StdioConfig};

let manager = ProcessManager::new();
let config = ProcessConfig::builder()
    .command("sleep".to_string())
    .args(vec!["10".to_string()])
    .stdout(StdioConfig::Null)
    .stderr(StdioConfig::Null)
    .build();

let id = manager.start("task", &config)?;

// Check the explicit state
match manager.state(id) {
    Some(ProcessState::Running) => println!("Process is running"),
    Some(ProcessState::Stopped) => println!("Process stopped normally"),
    Some(ProcessState::Crashed) => println!("Process crashed!"),
    Some(state) => println!("Process state: {}", state),
    None => println!("Process not found"),
}

// is_running() still works (delegates to state().is_alive())
assert_eq!(manager.is_running(id), Some(true));

// ProcessInfo also includes the state
let info = manager.get_info(id).unwrap();
println!("State: {}", info.state);
}

In ProcessExitEvent

When the reaper thread detects an exit, it sets the state field on ProcessExitEvent:

#![allow(unused)]
fn main() {
let event = receiver.recv_timeout(Duration::from_secs(5))?;
match event.state {
    ProcessState::Stopped => println!("Process {} exited normally", event.label),
    ProcessState::Crashed => println!("Process {} crashed", event.label),
    ProcessState::Failed => println!("Process {} failed (rate limit or spawn error)", event.label),
    _ => unreachable!(),
}
}