Skip to main content

proc_daemon/
lib.rs

1#![deny(missing_docs)]
2#![deny(unsafe_code)]
3#![warn(clippy::all)]
4#![warn(clippy::pedantic)]
5#![warn(clippy::nursery)]
6//! # proc-daemon: High-Performance Daemon Framework
7//!
8//! A foundational framework for building high-performance, resilient daemon services in Rust.
9//! Designed for enterprise applications requiring nanosecond-level performance, bulletproof
10//! reliability, and extreme concurrency.
11//!
12//! ## Key Features
13//!
14//! - **Zero-Copy Architecture**: Minimal allocations with memory pooling
15//! - **Runtime Agnostic**: Support for both Tokio and async-std via feature flags
16//! - **Cross-Platform**: First-class support for Linux, macOS, and Windows
17//! - **Graceful Shutdown**: Coordinated shutdown with configurable timeouts
18//! - **Signal Handling**: Robust cross-platform signal management
19//! - **Configuration**: Hot-reloadable configuration with multiple sources
20//! - **Structured Logging**: High-performance tracing with JSON support
21//! - **Subsystem Management**: Concurrent subsystem lifecycle management
22//! - **Enterprise Ready**: Built for 100,000+ concurrent operations
23//!
24//! ## Quick Start
25//!
26//! ```rust,compile_fail
27//! // This example is marked as compile_fail to prevent freezing in doctests
28//! use proc_daemon::{Daemon, Config, Result};
29//! use std::time::Duration;
30//!
31//! async fn my_service(mut shutdown: proc_daemon::ShutdownHandle) -> Result<()> {
32//!     // Limited iterations to prevent infinite loops
33//!     for _ in 0..3 {
34//!         tokio::select! {
35//!             _ = shutdown.cancelled() => {
36//!                 tracing::info!("Service shutting down gracefully");
37//!                 return Ok(());
38//!             }
39//!             _ = tokio::time::sleep(Duration::from_millis(10)) => {
40//!                 tracing::info!("Service working...");
41//!             }
42//!         }
43//!     }
44//!     Ok(())
45//! }
46//!
47//! #[tokio::main]
48//! async fn main() -> Result<()> {
49//!     let config = Config::new()?;
50//!     
51//!     // Set a timeout for the daemon to auto-shutdown
52//!     let daemon_handle = Daemon::builder(config)
53//!         .with_subsystem_fn("main", my_service)
54//!         .run()
55//!         .await?
56//!     
57//!     // Wait for a short time, then explicitly shut down
58//!     tokio::time::sleep(Duration::from_millis(100)).await;
59//!     daemon_handle.initiate_shutdown();
60//!     Ok(())
61//! }
62//! ```
63//
64//! ```rust,ignore
65//! // This example is marked as ignore to prevent freezing in doctests
66//! use proc_daemon::{Daemon, Config, Result};
67//! use std::time::Duration;
68//!
69//! async fn my_service(mut shutdown: proc_daemon::ShutdownHandle) -> Result<()> {
70//!     // Use a counter to avoid infinite loops
71//!     let mut counter = 0;
72//!     while counter < 3 {
73//!         tokio::select! {
74//!             _ = shutdown.cancelled() => {
75//!                 tracing::info!("Service shutting down gracefully");
76//!                 break;
77//!             }
78//!             _ = tokio::time::sleep(Duration::from_millis(10)) => {
79//!                 tracing::info!("Service working...");
80//!                 counter += 1;
81//!             }
82//!         }
83//!     }
84//!     Ok(())
85//! }
86//!
87//! #[tokio::main]
88//! async fn main() -> Result<()> {
89//!     let config = Config::new()?;
90//!     
91//!     // Auto-shutdown after a brief time
92//!     tokio::spawn(async {
93//!         tokio::time::sleep(Duration::from_millis(50)).await;
94//!         std::process::exit(0); // Force exit to prevent hanging
95//!     });
96//!     
97//!     Daemon::builder(config)
98//!         .with_subsystem_fn("main", my_service)
99//!         .run()
100//!         .await
101//! }
102//! ```
103
104// Optional global allocator: mimalloc
105// Enabled only when the 'mimalloc' feature is set
106#[cfg(feature = "mimalloc")]
107#[global_allocator]
108static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
109
110// Private modules
111mod config;
112mod daemon;
113mod error;
114mod pool;
115
116// Public modules
117pub mod coord;
118pub mod lock;
119pub mod resources;
120pub mod shutdown;
121pub mod signal;
122pub mod subsystem;
123
124// Public exports
125pub use config::{Config, LogLevel};
126pub use daemon::{Daemon, DaemonBuilder};
127pub use error::{Error, Result};
128pub use pool::*;
129pub use shutdown::{ShutdownHandle, ShutdownReason};
130pub use subsystem::{RestartPolicy, Subsystem, SubsystemId};
131
132#[cfg(feature = "metrics")]
133pub mod metrics;
134
135#[cfg(feature = "profiling")]
136pub mod profiling;
137
138#[cfg(feature = "ipc")]
139pub mod ipc;
140
141/// High-resolution timing helpers (opt-in)
142///
143/// When the `high-res-timing` feature is enabled, provides an ultra-fast
144/// monotonically increasing clock via `quanta`.
145#[cfg(feature = "high-res-timing")]
146pub mod timing {
147    use quanta::Clock;
148    pub use quanta::Instant;
149
150    static CLOCK: std::sync::LazyLock<Clock> = std::sync::LazyLock::new(Clock::new);
151
152    /// Returns a high-resolution Instant using a cached `quanta::Clock`.
153    #[inline]
154    pub fn now() -> Instant {
155        CLOCK.now()
156    }
157}
158
159/// Scheduler hint hooks (opt-in)
160///
161/// When the `scheduler-hints` feature is enabled, exposes best-effort functions to
162/// apply light-weight scheduler tuning (process niceness) where supported. All
163/// operations are non-fatal; failures are logged at debug level.
164#[cfg(feature = "scheduler-hints")]
165pub mod scheduler {
166    use tracing::{debug, info};
167
168    /// Apply process-level scheduler hints.
169    ///
170    /// Apply process-level hints.
171    ///
172    /// Default: no-op. If `scheduler-hints-unix` is enabled on Unix, attempts a
173    /// best-effort niceness reduction.
174    pub fn apply_process_hints(config: &crate::config::Config) {
175        let name = &config.name;
176        #[cfg(all(feature = "scheduler-hints-unix", unix))]
177        {
178            use std::process::Command;
179            // Try renicing current process by -5. This typically needs elevated privileges.
180            let delta = "-5";
181            let pid = std::process::id().to_string();
182            let out = Command::new("renice")
183                .args(["-n", delta, "-p", pid.as_str()])
184                .output();
185            match out {
186                Ok(res) if res.status.success() => {
187                    info!(%name, delta, "scheduler-hints: renice applied");
188                    return;
189                }
190                Ok(res) => {
191                    let code = res.status.code();
192                    let stderr = String::from_utf8_lossy(&res.stderr);
193                    debug!(%name, delta, code, stderr = %stderr, "scheduler-hints: renice failed (best-effort)");
194                }
195                Err(e) => {
196                    debug!(%name, delta, error = %e, "scheduler-hints: renice invocation failed (best-effort)");
197                }
198            }
199        }
200        debug!(%name, "scheduler-hints: apply_process_hints (noop)");
201    }
202
203    /// Apply runtime-level scheduler hints.
204    ///
205    /// Placeholder for future integration (e.g., per-thread QoS/priority, affinity).
206    pub fn apply_runtime_hints() {
207        #[cfg(all(feature = "scheduler-hints-unix", target_os = "linux"))]
208        {
209            use nix::sched::{sched_setaffinity, CpuSet};
210            use nix::unistd::Pid;
211
212            // Best-effort: allow running on all available CPUs (explicitly set mask)
213            let mut set = CpuSet::new();
214            let cpus = num_cpus::get();
215            for cpu in 0..cpus {
216                let _ = set.set(cpu);
217            }
218            match sched_setaffinity(Pid::from_raw(0), &set) {
219                Ok(()) => debug!(
220                    cpus,
221                    "scheduler-hints: setaffinity applied to current process threads (best-effort)"
222                ),
223                Err(e) => debug!(error = %e, "scheduler-hints: setaffinity failed (best-effort)"),
224            }
225        }
226
227        #[cfg(not(all(feature = "scheduler-hints-unix", target_os = "linux")))]
228        {
229            debug!("scheduler-hints: apply_runtime_hints (noop)");
230        }
231    }
232}
233
234/// Version of the proc-daemon library
235pub const VERSION: &str = env!("CARGO_PKG_VERSION");
236
237/// Default shutdown timeout in milliseconds
238pub const DEFAULT_SHUTDOWN_TIMEOUT_MS: u64 = 5000;
239
240/// Default configuration file name
241pub const DEFAULT_CONFIG_FILE: &str = "daemon.toml";