#[cfg(target_os = "linux")] mod memory { use std::sync::atomic::{AtomicBool, Ordering}; static MLOCKALL_CALLED: AtomicBool = AtomicBool::new(false); static MLOCKALL_SUCCESS: AtomicBool = AtomicBool::new(false); /// Locks all current and future memory pages to prevent page faults during RT execution. /// Must be called BEFORE spawning any threads for maximum effectiveness. pub fn lock_memory() -> bool { if MLOCKALL_CALLED.swap(true, Ordering::SeqCst) { return MLOCKALL_SUCCESS.load(Ordering::SeqCst); } let result = unsafe { libc::mlockall(libc::MCL_CURRENT | libc::MCL_FUTURE) }; if result == 0 { MLOCKALL_SUCCESS.store(true, Ordering::SeqCst); true } else { let errno = std::io::Error::last_os_error(); eprintln!("[cagire] mlockall failed: {errno}"); eprintln!("[cagire] Memory locking disabled. For best RT performance on Linux:"); eprintln!("[cagire] 1. Add user to 'audio' group: sudo usermod -aG audio $USER"); eprintln!("[cagire] 2. Add to /etc/security/limits.conf:"); eprintln!("[cagire] @audio - memlock unlimited"); eprintln!("[cagire] 3. Log out and back in"); false } } } #[cfg(target_os = "linux")] pub use memory::lock_memory; #[cfg(not(target_os = "linux"))] pub fn lock_memory() -> bool { true } /// Attempts to set realtime scheduling priority for the current thread. /// Returns true if RT priority was successfully set, false otherwise. #[cfg(target_os = "macos")] pub fn set_realtime_priority() -> bool { // macOS: use THREAD_TIME_CONSTRAINT_POLICY for true RT scheduling. // This is the same mechanism CoreAudio uses for its audio threads. // SCHED_FIFO/RR require root on macOS, but time constraint policy does not. unsafe { let thread = libc::pthread_self(); #[repr(C)] struct ThreadTimeConstraintPolicy { period: u32, computation: u32, constraint: u32, preemptible: i32, } const THREAD_TIME_CONSTRAINT_POLICY_ID: u32 = 2; const THREAD_TIME_CONSTRAINT_POLICY_COUNT: u32 = 4; // ~1ms period at ~1GHz mach_absolute_time ticks (typical for audio) let policy = ThreadTimeConstraintPolicy { period: 1_000_000, computation: 500_000, constraint: 1_000_000, preemptible: 1, }; extern "C" { fn thread_policy_set( thread: libc::pthread_t, flavor: u32, policy_info: *const ThreadTimeConstraintPolicy, count: u32, ) -> i32; } let result = thread_policy_set( thread, THREAD_TIME_CONSTRAINT_POLICY_ID, &policy, THREAD_TIME_CONSTRAINT_POLICY_COUNT, ); result == 0 } } /// Attempts to set realtime scheduling priority for the current thread. /// Returns true if RT priority was successfully set, false otherwise. /// /// On Linux, this requires either: /// - CAP_SYS_NICE capability, or /// - Configured rtprio limits in /etc/security/limits.conf: /// @audio - rtprio 95 /// @audio - memlock unlimited #[cfg(all(target_os = "linux", feature = "cli"))] pub fn set_realtime_priority() -> bool { use thread_priority::unix::{ set_thread_priority_and_policy, thread_native_id, NormalThreadSchedulePolicy, RealtimeThreadSchedulePolicy, ThreadSchedulePolicy, }; use thread_priority::ThreadPriority; let tid = thread_native_id(); let fifo = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Fifo); if set_thread_priority_and_policy(tid, ThreadPriority::Max, fifo).is_ok() { return true; } let rr = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::RoundRobin); if set_thread_priority_and_policy(tid, ThreadPriority::Max, rr).is_ok() { return true; } let _ = set_thread_priority_and_policy( tid, ThreadPriority::Max, ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Other), ); unsafe { libc::setpriority(libc::PRIO_PROCESS, 0, -20); } false } #[cfg(all(target_os = "linux", not(feature = "cli")))] pub fn set_realtime_priority() -> bool { false } #[cfg(not(any(unix, target_os = "windows")))] pub fn set_realtime_priority() -> bool { false } #[cfg(all(target_os = "windows", feature = "cli"))] pub fn set_realtime_priority() -> bool { use thread_priority::{set_current_thread_priority, ThreadPriority}; set_current_thread_priority(ThreadPriority::Max).is_ok() } #[cfg(all(target_os = "windows", not(feature = "cli")))] pub fn set_realtime_priority() -> bool { false } /// High-precision sleep using clock_nanosleep on Linux. /// Uses monotonic clock for jitter-free sleeping. #[cfg(target_os = "linux")] pub fn precise_sleep_us(micros: u64) { let duration_ns = micros * 1000; let ts = libc::timespec { tv_sec: (duration_ns / 1_000_000_000) as i64, tv_nsec: (duration_ns % 1_000_000_000) as i64, }; unsafe { libc::clock_nanosleep(libc::CLOCK_MONOTONIC, 0, &ts, std::ptr::null_mut()); } } #[cfg(not(target_os = "linux"))] pub fn precise_sleep_us(micros: u64) { std::thread::sleep(std::time::Duration::from_micros(micros)); }