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 once_cell::sync::Lazy;
148    use quanta::Clock;
149    pub use quanta::Instant;
150
151    static CLOCK: Lazy<Clock> = Lazy::new(Clock::new);
152
153    /// Returns a high-resolution Instant using a cached `quanta::Clock`.
154    #[inline]
155    pub fn now() -> Instant {
156        CLOCK.now()
157    }
158}
159
160/// Scheduler hint hooks (opt-in)
161///
162/// When the `scheduler-hints` feature is enabled, exposes best-effort functions to
163/// apply light-weight scheduler tuning (process niceness) where supported. All
164/// operations are non-fatal; failures are logged at debug level.
165#[cfg(feature = "scheduler-hints")]
166pub mod scheduler {
167    use tracing::{debug, info};
168
169    /// Apply process-level scheduler hints.
170    ///
171    /// Apply process-level hints.
172    ///
173    /// Default: no-op. If `scheduler-hints-unix` is enabled on Unix, attempts a
174    /// best-effort niceness reduction.
175    pub fn apply_process_hints(config: &crate::config::Config) {
176        let name = &config.name;
177        #[cfg(all(feature = "scheduler-hints-unix", unix))]
178        {
179            use std::process::Command;
180            // Try renicing current process by -5. This typically needs elevated privileges.
181            let delta = "-5";
182            let pid = std::process::id().to_string();
183            let out = Command::new("renice")
184                .args(["-n", delta, "-p", pid.as_str()])
185                .output();
186            match out {
187                Ok(res) if res.status.success() => {
188                    info!(%name, delta, "scheduler-hints: renice applied");
189                    return;
190                }
191                Ok(res) => {
192                    let code = res.status.code();
193                    let stderr = String::from_utf8_lossy(&res.stderr);
194                    debug!(%name, delta, code, stderr = %stderr, "scheduler-hints: renice failed (best-effort)");
195                }
196                Err(e) => {
197                    debug!(%name, delta, error = %e, "scheduler-hints: renice invocation failed (best-effort)");
198                }
199            }
200        }
201        debug!(%name, "scheduler-hints: apply_process_hints (noop)");
202    }
203
204    /// Apply runtime-level scheduler hints.
205    ///
206    /// Placeholder for future integration (e.g., per-thread QoS/priority, affinity).
207    pub fn apply_runtime_hints() {
208        #[cfg(all(feature = "scheduler-hints-unix", target_os = "linux"))]
209        {
210            use nix::sched::{sched_setaffinity, CpuSet};
211            use nix::unistd::Pid;
212
213            // Best-effort: allow running on all available CPUs (explicitly set mask)
214            let mut set = CpuSet::new();
215            let cpus = num_cpus::get();
216            for cpu in 0..cpus {
217                let _ = set.set(cpu);
218            }
219            match sched_setaffinity(Pid::from_raw(0), &set) {
220                Ok(()) => debug!(
221                    cpus,
222                    "scheduler-hints: setaffinity applied to current process threads (best-effort)"
223                ),
224                Err(e) => debug!(error = %e, "scheduler-hints: setaffinity failed (best-effort)"),
225            }
226        }
227
228        #[cfg(not(all(feature = "scheduler-hints-unix", target_os = "linux")))]
229        {
230            debug!("scheduler-hints: apply_runtime_hints (noop)");
231        }
232    }
233}
234
235/// Version of the proc-daemon library
236pub const VERSION: &str = env!("CARGO_PKG_VERSION");
237
238/// Default shutdown timeout in milliseconds
239pub const DEFAULT_SHUTDOWN_TIMEOUT_MS: u64 = 5000;
240
241/// Default configuration file name
242pub const DEFAULT_CONFIG_FILE: &str = "daemon.toml";