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