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";