Skip to content

jamesgober/proc-daemon

Repository files navigation

Rust logo

Process Daemon
RUST DAEMON FRAMEWORK

Crates.io   Crates.io Downloads   License   docs.rs   GitHub CI

A foundational framework for building high-performance, resilient daemon services in Rust. Designed for enterprise applications requiring nanosecond-level performance, bulletproof reliability, and extreme concurrency.

Status

✅ Stable Release — Production-ready with zero critical vulnerabilities, comprehensive automated testing (36 unit + 5 integration tests passing on default features; 39 + 5 + 4 doc tests under --all-features), and cross-platform validation.

Latest: v1.1.2 — see the release notes and CHANGELOG.md. Prior releases: v1.1.1 (docs.rs build fix) · v1.1.0 (performance + ergonomics) · v1.0.1 (maintenance + audit).

Features

Core Capabilities

  • Zero-Copy Architecture: Minimal allocations with memory pooling for maximum performance
  • Runtime Agnostic: First-class support for both Tokio and async-std via feature flags
  • Cross-Platform: Native support for Linux, macOS, and Windows with platform-specific optimizations
  • Graceful Shutdown: Coordinated shutdown with configurable timeouts and subsystem awareness
  • Signal Handling: Robust cross-platform signal management (SIGTERM, SIGINT, SIGQUIT, SIGHUP, Windows console events)

Advanced Features

  • Subsystem Management: Concurrent subsystem lifecycle management with health checks and auto-restart
  • Configuration Hot-Reload: Dynamic configuration updates without service interruption
  • Structured Logging: High-performance tracing with JSON support and log rotation
  • Metrics Collection: Built-in performance monitoring and resource tracking
  • Memory Safety: Safe Rust by default (#![deny(unsafe_code)]); Windows monitoring uses guarded unsafe when the windows-monitoring feature is enabled

Enterprise Ready

  • High Concurrency: Built for 100,000+ concurrent operations
  • Resource Management: Intelligent memory pooling and NUMA awareness
  • Health Monitoring: Comprehensive subsystem health checks and diagnostics
  • Production Tested: Battle-tested patterns from high-scale deployments



Installation

Add this to your Cargo.toml:

[dependencies]
proc-daemon = "1.1.2"

# Optional features
proc-daemon = { version = "1.1.2", features = ["full"] }

Rust Version: Requires Rust 1.82.0 or later

Feature Flags

Feature Description Default
tokio Tokio runtime support
async-std async-std runtime support (unmaintained; best-effort legacy)
metrics Performance metrics collection and introspection
console Enhanced console output
json-logs JSON structured logging
config-watch Configuration hot-reloading
mmap-config Memory-mapped config file loading (TOML fast-path, safe fallback)
mimalloc Use mimalloc as global allocator
high-res-timing High-resolution timing via quanta
scheduler-hints Enable scheduler tuning hooks (no-op by default)
scheduler-hints-unix Best-effort Unix niceness adjustment (uses renice; no-op without privileges)
lockfree-coordination Lock-free coordination/events via crossbeam-channel
profiling Optional CPU profiling via pprof (Unix-only — target-gated; inert on Windows)
heap-profiling Optional heap profiling via dhat (cross-platform)
full All features enabled

Note: async-std is discontinued upstream; support here is best-effort and intended for existing users (slated for removal in v2.0.0). The profiling feature is target-gated to Unix — pprof relies on POSIX libc types (pthread_t, siginfo_t, ucontext_t) and will not be compiled on Windows. The heap-profiling feature (dhat) is cross-platform.

Quick Start

Simple Daemon

use proc_daemon::{Daemon, ShutdownHandle};
use std::time::Duration;

async fn my_service(mut shutdown: ShutdownHandle) -> proc_daemon::Result<()> {
    let mut counter = 0;
    loop {
        tokio::select! {
            () = shutdown.cancelled() => {
                tracing::info!("Service shutting down after {counter} iterations");
                break;
            }
            () = tokio::time::sleep(Duration::from_secs(1)) => {
                counter += 1;
                tracing::info!("Service running: iteration {counter}");
            }
        }
    }
    Ok(())
}

#[tokio::main]
async fn main() -> proc_daemon::Result<()> {
    // v1.1.0: `Daemon::new()` is the infallible default-config shortcut.
    // For explicit config, use `Daemon::builder(config)` instead.
    Daemon::new()
        .with_task("my_service", my_service)
        .run()
        .await
}

High-Resolution Timing (optional)

Enable the high-res-timing feature to access a fast, monotonic clock backed by quanta:

[dependencies]
proc-daemon = { version = "1.1.2", features = ["high-res-timing"] }
#[cfg(feature = "high-res-timing")]
{
    let t0 = proc_daemon::timing::now();
    // ... work ...
    let t1 = proc_daemon::timing::now();
    let dt = t1.duration_since(t0);
    println!("elapsed: {:?}", dt);
}

Mimalloc Global Allocator (optional)

Enable the mimalloc feature to switch the global allocator for potential performance wins in allocation-heavy workloads:

[dependencies]
proc-daemon = { version = "1.1.2", features = ["mimalloc"] }

No code changes are required—proc-daemon sets the global allocator when the feature is enabled.

Lock-free Coordination (optional)

Enable the lockfree-coordination feature to use a lock-free MPMC channel for coordination. This exposes a small channel facade and optional subsystem events for state changes.

[dependencies]
proc-daemon = { version = "1.1.2", features = ["lockfree-coordination"] }

APIs:

  • proc_daemon::coord::chan::{unbounded, try_recv} — Uniform API over crossbeam-channel (enabled) or std::sync::mpsc (fallback).
  • SubsystemManager::enable_events() and SubsystemManager::try_next_event() — non-blocking event polling.
  • SubsystemManager::subscribe_events() — get a Receiver<SubsystemEvent> to poll from another task when events are enabled.

Event type:

SubsystemEvent::StateChanged { id, name, state, at }

Multi-Subsystem Daemon

use proc_daemon::{Config, Daemon, RestartPolicy, ShutdownHandle, Subsystem};
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;

// Define a custom subsystem
struct HttpServer {
    port: u16,
}

impl Subsystem for HttpServer {
    fn run(
        &self,
        mut shutdown: ShutdownHandle,
    ) -> Pin<Box<dyn Future<Output = proc_daemon::Result<()>> + Send>> {
        let port = self.port;
        Box::pin(async move {
            tracing::info!("HTTP server starting on port {port}");
            loop {
                tokio::select! {
                    () = shutdown.cancelled() => {
                        tracing::info!("HTTP server shutting down");
                        break;
                    }
                    () = tokio::time::sleep(Duration::from_millis(100)) => {
                        // Handle HTTP requests here
                    }
                }
            }
            Ok(())
        })
    }

    fn name(&self) -> &str {
        "http_server"
    }

    // Restart with exponential backoff (1s → 60s, max 5 attempts).
    fn restart_policy(&self) -> RestartPolicy {
        RestartPolicy::ExponentialBackoff {
            initial_delay: Duration::from_secs(1),
            max_delay: Duration::from_secs(60),
            max_attempts: 5,
        }
    }
}

async fn background_worker(mut shutdown: ShutdownHandle) -> proc_daemon::Result<()> {
    loop {
        tokio::select! {
            () = shutdown.cancelled() => break,
            () = tokio::time::sleep(Duration::from_secs(5)) => {
                tracing::info!("Background work completed");
            }
        }
    }
    Ok(())
}

#[tokio::main]
async fn main() -> proc_daemon::Result<()> {
    // Note: the builder timeout setters return `Result<Self>` (timeouts are
    // validated relative to each other: graceful < force < kill). `?` chains
    // them cleanly inside `main`.
    let config = Config::builder()
        .name("multi-subsystem-daemon")
        .shutdown_timeout(Duration::from_secs(30))?
        .worker_threads(4)
        .build()?;

    Daemon::builder(config)
        .with_subsystem(HttpServer { port: 8080 })
        .with_task("background_worker", background_worker)
        .run()
        .await
}

Configuration

Programmatic Configuration

use proc_daemon::{Config, LogLevel};
use std::time::Duration;

# fn build_cfg() -> proc_daemon::Result<Config> {
let config = Config::builder()
    .name("my-daemon")
    .log_level(LogLevel::Info)
    .json_logging(true)
    // Timeouts are validated relative to each other: graceful < force < kill.
    // The setters return `Result<Self>`; `?` chains them inside a Result fn.
    .shutdown_timeout(Duration::from_secs(30))?
    .force_shutdown_timeout(Duration::from_secs(45))?
    .kill_timeout(Duration::from_secs(60))?
    .worker_threads(8)
    .enable_metrics(true)
    .hot_reload(true)
    .build()?;
# Ok(config)
# }

File Configuration (TOML)

Create a daemon.toml file:

name = "my-production-daemon"

[logging]
level = "info"
json = false
color = true
file = "/var/log/my-daemon.log"

[shutdown]
timeout_ms = 30000
force_timeout_ms = 45000
kill_timeout_ms = 60000

[performance]
worker_threads = 0  # auto-detect
thread_pinning = false
memory_pool_size = 1048576
numa_aware = false
lock_free = true

[monitoring]
enable_metrics = true
metrics_interval_ms = 1000
health_checks = true

Load the configuration:

let config = Config::load_from_file("daemon.toml")?;

Environment Variables

All configuration options can be overridden with environment variables using the DAEMON_ prefix:

export DAEMON_NAME="env-daemon"
export DAEMON_LOGGING_LEVEL="debug"
export DAEMON_SHUTDOWN_TIMEOUT_MS="60000"
export DAEMON_PERFORMANCE_WORKER_THREADS="16"

Advanced Usage

Custom Subsystems with Health Checks

use proc_daemon::{ShutdownHandle, Subsystem};
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

struct DatabasePool {
    connections: Arc<AtomicUsize>,
}

impl Subsystem for DatabasePool {
    fn run(
        &self,
        _shutdown: ShutdownHandle,
    ) -> Pin<Box<dyn Future<Output = proc_daemon::Result<()>> + Send>> {
        let _connections = Arc::clone(&self.connections);
        Box::pin(async move {
            // Database pool management logic …
            Ok(())
        })
    }

    fn name(&self) -> &str {
        "database_pool"
    }

    // Health checks are polled every `monitoring.health_check_interval_ms`
    // by the daemon main loop when `monitoring.health_checks = true`.
    fn health_check(&self) -> Option<Box<dyn Fn() -> bool + Send + Sync>> {
        let connections = Arc::clone(&self.connections);
        Some(Box::new(move || connections.load(Ordering::Acquire) > 0))
    }
}

Metrics Collection

#[cfg(feature = "metrics")]
use proc_daemon::metrics::MetricsCollector;

let collector = MetricsCollector::new();

// Increment counters
collector.increment_counter("requests_total", 1);

// Set gauge values
collector.set_gauge("active_connections", 42);

// Record timing histograms
collector.record_histogram("request_duration", Duration::from_millis(150));

// Get metrics snapshot
let snapshot = collector.get_metrics();
println!("Uptime: {:?}", snapshot.uptime);

Signal Handling Configuration

use proc_daemon::signal::SignalConfig;
use proc_daemon::Daemon;

# async fn example() -> proc_daemon::Result<()> {
let signal_config = SignalConfig::new()
    .with_sighup()                       // Enable SIGHUP handling
    .without_sigint()                    // Disable SIGINT
    .with_custom_handler(12, "Custom signal");

Daemon::new()
    .with_signal_config(signal_config)
    .run()
    .await
# }

Architecture

Zero-Copy Design

proc-daemon is built around zero-copy principles:

  • Arc-based sharing: Configuration and state shared via Arc to avoid cloning
  • Lock-free coordination: Uses atomic operations and lock-free data structures
  • Memory pooling: Pre-allocated memory pools for high-frequency operations
  • Efficient serialization: Direct memory mapping for configuration loading

Subsystem Lifecycle

graph TD
    A[Register] --> B[Starting]
    B --> C[Running]
    C --> D[Health Check]
    D --> C
    C --> E[Stopping]
    E --> F[Stopped]
    F --> G[Restart?]
    G -->|Yes| B
    G -->|No| H[Removed]
    C --> I[Failed]
    I --> G
Loading

Shutdown Coordination

proc-daemon implements a sophisticated shutdown coordination system:

  1. Signal Reception: Cross-platform signal handling
  2. Graceful Notification: All subsystems notified simultaneously
  3. Coordinated Shutdown: Subsystems shut down in dependency order
  4. Timeout Management: Configurable graceful and force timeouts
  5. Resource Cleanup: Automatic cleanup of resources and handles

Testing

Run the test suite:

# Run all tests
cargo test

# Run tests with all features
cargo test --all-features

# Run integration tests
cargo test --test integration

# Run benchmarks
cargo bench


Hot-Reload (optional)

Enable config-watch to live-reload daemon.toml at runtime (optionally combine with mmap-config for fast TOML loading). The daemon maintains a live snapshot accessible via Daemon::config_snapshot().

Run the example:

cargo run --example hot_reload --features "tokio config-watch toml mmap-config"

Notes:

  • Place daemon.toml in the working directory.
  • The watcher starts automatically when Config.hot_reload = true.


Examples

All runnable examples are available in the examples/ directory. Here are the key ones:

simple.rs

A minimal daemon that demonstrates basic setup and graceful shutdown:

cargo run --example simple --features tokio

comprehensive.rs

Multi-subsystem daemon showcasing:

  • Custom subsystem implementations
  • Health checks
  • Restart policies
  • Metrics collection
cargo run --example comprehensive --features tokio,metrics

hot_reload.rs

Configuration hot-reload demonstration that watches daemon.toml:

cargo run --example hot_reload --features "tokio config-watch toml mmap-config"

Edit daemon.toml to see the daemon pick up changes without restart.

metrics_server.rs

Metrics collection and introspection example:

cargo run --example metrics_server --features "tokio metrics"


PERFORMANCE

proc-daemon is designed for extreme performance:

Running Benchmarks

cargo bench

Typical performance:

  • Daemon Creation: ~1-5μs
  • Subsystem Registration: ~500ns per subsystem
  • Shutdown Coordination: ~10-50μs for 100 subsystems
  • Signal Handling: ~100ns latency
  • Metrics Collection: ~10ns per operation

Memory Usage

  • Base daemon: ~1-2MB
  • Per subsystem: ~4-8KB
  • Configuration: ~1-4KB
  • Signal handling: ~512B


🔗 See PERFORMANCE.md for up-to-date benchmarks, metrics, and version-over-version improvements.




Security

Security Posture (v1.1.1)

  • Zero unresolved vulnerabilities: clean cargo audit. Three documented allow-list entries cover the optional async-std feature path (legacy, removal in v2.0.0) and a dev-only rand advisory via proptest — none reach production code paths.
  • pprof correctness: bumped to 0.14 in v1.0.1, resolving RUSTSEC-2024-0408 (unsound std::slice::from_raw_parts usage in the optional CPU profiler).
  • Memory Safety: Safe Rust by default (#![deny(unsafe_code)]); Windows monitoring uses guarded unsafe only when explicitly enabled via the windows-monitoring feature.
  • No lock poisoning in hot paths: v1.1.0 migrated remaining std::sync::Mutex/RwLock sites to parking_lot equivalents, which don't poison — eliminating an entire class of latent failure modes inside subsystem and resource tracking.
  • Signal Safety: async signal handling routes platform signals into the shutdown coordinator without holding locks in handlers.
  • Resource Limits: configurable shutdown/force/kill timeouts cap any single subsystem's ability to block teardown.
  • Graceful Degradation: continues operating when individual subsystems fail; restart policies are explicit per subsystem.

Running Security Checks

# Verify no unallowed vulnerabilities
cargo audit

# Static analysis
cargo clippy --all-features --all-targets -- -D warnings

# Check for unsafe code (install with: cargo install cargo-geiger)
cargo geiger

See .cargo/audit.toml for documented allowed vs. disallowed vulnerabilities.

Development Setup

git clone https://github.com/jamesgober/proc-daemon.git
cd proc-daemon

# Build with all features enabled
cargo build --all-features

# Run all tests
cargo test --all-features

# Run linting checks
cargo clippy --all-features --all-targets -- -D warnings

# Run benchmarks
cargo bench

# Check security vulnerabilities
cargo audit

Supported Platforms

  • Linux: Full support, optimized for production
  • macOS: Full support, verified on x86_64 and ARM64
  • Windows: Supported, with caveats listed below
  • FreeBSD: Likely works but not regularly tested

Windows Note: The profiling feature is target-gated to Unix (v1.0.1+) — pprof relies on POSIX libc types (pthread_t, siginfo_t, ucontext_t). On Windows the feature is inert (not compiled). Heap profiling (heap-profiling, via dhat) is cross-platform. The scheduler-hints-unix feature is a no-op on Windows; windows-monitoring provides equivalent Win32 ToolHelp-based monitoring.



Documentation:



Acknowledgments

  • Inspired by production daemon patterns from high-scale deployments
  • Built on the excellent Rust async ecosystem (Tokio; async-std legacy support)
  • Configuration management powered by Figment
  • Logging via the tracing ecosystem



DEVELOPMENT & CONTRIBUTION

We welcome contributions! Please open an issue or pull request on the GitHub repository.


⚖️ License

Licensed under the Apache License, version 2.0 (the "License"); you may not use this software, including, but not limited to the source code, media files, ideas, techniques, or any other associated property or concept belonging to, associated with, or otherwise packaged with this software except in compliance with the License.

You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the LICENSE file included with this project for the specific language governing permissions and limitations under the License.


COPYRIGHT © 2026 JAMES GOBER.

About

A high-performance, cross-platform daemon framework for Rust. Built with an async-native architecture and robust graceful shutdown for creating resilient, production-grade services.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors