#![allow(unsafe_code)]
#![allow(clippy::arithmetic_side_effects)]
use crate::control_types::ControlSignal;
use crate::llmosafe_kernel::{KernelError, Synapse};
use std::fs;
use std::io::{BufRead, BufReader};
use std::thread;
use std::time::Duration;
#[derive(Debug, Clone, Default)]
pub struct EnvironmentalVitals {
pub iowait: u64,
pub load_avg: f64,
pub vitals_available: bool,
}
impl EnvironmentalVitals {
pub fn capture() -> Self {
let iowait = Self::read_iowait();
let load_avg = Self::read_loadavg();
let vitals_available = iowait.is_some() && load_avg.is_some();
Self {
iowait: iowait.unwrap_or(0),
load_avg: load_avg.unwrap_or(0.0),
vitals_available,
}
}
#[cfg(target_os = "linux")]
fn read_iowait() -> Option<u64> {
let content = match fs::read_to_string("/proc/stat") {
Ok(c) => c,
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"Cannot read /proc/stat for iowait: {}",
e
);
return None;
}
};
let line = match content.lines().next() {
Some(l) => l,
None => {
tracing::warn!(
target: "llmosafe::body",
"iowait field missing or unparseable in /proc/stat"
);
return None;
}
};
let iowait_str = match line.split_whitespace().nth(5) {
Some(s) => s,
None => {
tracing::warn!(
target: "llmosafe::body",
"iowait field missing or unparseable in /proc/stat"
);
return None;
}
};
match iowait_str.parse::<u64>() {
Ok(v) => Some(v),
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"iowait field missing or unparseable in /proc/stat: {}",
e
);
None
}
}
}
#[cfg(not(target_os = "linux"))]
fn read_iowait() -> Option<u64> {
None
}
#[cfg(target_os = "linux")]
fn read_loadavg() -> Option<f64> {
let content = match fs::read_to_string("/proc/loadavg") {
Ok(c) => c,
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"Cannot read /proc/loadavg: {}",
e
);
return None;
}
};
let line = match content.lines().next() {
Some(l) => l,
None => {
tracing::warn!(
target: "llmosafe::body",
"loadavg field missing or unparseable in /proc/loadavg"
);
return None;
}
};
let first_part = match line.split_whitespace().next() {
Some(s) => s,
None => {
tracing::warn!(
target: "llmosafe::body",
"loadavg field missing or unparseable in /proc/loadavg"
);
return None;
}
};
match first_part.parse::<f64>() {
Ok(v) => Some(v),
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"loadavg field missing or unparseable in /proc/loadavg: {}",
e
);
None
}
}
}
#[cfg(not(target_os = "linux"))]
fn read_loadavg() -> Option<f64> {
None
}
}
#[derive(Debug, Clone, Copy)]
pub struct BodyOutput {
pub error_body: f32,
pub pressure: u8,
pub is_exhausted: bool,
}
impl ControlSignal for BodyOutput {
fn error(&self) -> f32 {
self.error_body
}
fn setpoint(&self) -> f32 {
0.0
}
}
#[derive(Debug, Clone)]
pub struct ResourceGuard {
memory_ceiling_bytes: usize,
#[cfg(any(test, feature = "testing"))]
raw_entropy_override: Option<u16>,
#[cfg(any(test, feature = "testing"))]
pressure_override: Option<u8>,
}
impl ResourceGuard {
pub fn new(memory_ceiling_bytes: usize) -> Self {
Self {
memory_ceiling_bytes,
#[cfg(any(test, feature = "testing"))]
raw_entropy_override: None,
#[cfg(any(test, feature = "testing"))]
pressure_override: None,
}
}
#[cfg(any(test, feature = "testing"))]
pub fn for_testing(ceiling_bytes: usize, raw_entropy_val: u16, pressure_val: u8) -> Self {
Self {
memory_ceiling_bytes: ceiling_bytes,
raw_entropy_override: Some(raw_entropy_val),
pressure_override: Some(pressure_val),
}
}
pub fn raw_entropy(&self) -> u16 {
#[cfg(any(test, feature = "testing"))]
if let Some(v) = self.raw_entropy_override {
return v;
}
let current_rss = Self::try_current_rss_bytes().unwrap_or_else(|| {
tracing::warn!(
target: "llmosafe::body",
"RSS measurement unavailable in raw_entropy(); substituting memory_ceiling_bytes ({}) as fail-closed value",
self.memory_ceiling_bytes
);
self.memory_ceiling_bytes
});
let rss_ratio = if self.memory_ceiling_bytes > 0 {
(current_rss as f64 / self.memory_ceiling_bytes as f64).min(1.0)
} else {
1.0
};
let vitals = EnvironmentalVitals::capture();
let load_ratio = if vitals.vitals_available {
(vitals.load_avg / 10.0).min(1.0)
} else {
tracing::warn!(
target: "llmosafe::body",
"Environmental vitals unavailable (no /proc access) in raw_entropy(); using fail-closed load_ratio=1.0"
);
1.0
};
#[cfg(target_os = "linux")]
let iowait_ratio = Self::delta_iowait_ratio();
#[cfg(not(target_os = "linux"))]
let iowait_ratio = 0.0_f64;
let weighted_score = (rss_ratio * 500.0) + (iowait_ratio * 250.0) + (load_ratio * 250.0);
weighted_score.min(1000.0) as u16
}
pub fn pressure(&self) -> u8 {
#[cfg(any(test, feature = "testing"))]
if let Some(v) = self.pressure_override {
return v;
}
if self.memory_ceiling_bytes == 0 {
return 100;
}
let current_rss = Self::try_current_rss_bytes().unwrap_or_else(|| {
tracing::warn!(
target: "llmosafe::body",
"RSS measurement unavailable in pressure(); substituting memory_ceiling_bytes ({}) as fail-closed value",
self.memory_ceiling_bytes
);
self.memory_ceiling_bytes
});
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
(ratio * 100.0).min(100.0) as u8
}
#[must_use = "ignoring the safety check defeats the purpose of the guard"]
pub fn check(&self) -> Result<Synapse, KernelError> {
if self.memory_ceiling_bytes == 0 {
return Err(KernelError::ResourceExhaustion);
}
#[cfg(any(test, feature = "testing"))]
let current_rss = if self.pressure_override.is_some() {
self.memory_ceiling_bytes / 2
} else {
match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
}
};
#[cfg(not(any(test, feature = "testing")))]
let current_rss = match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
};
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
if ratio >= 1.0 {
return Err(KernelError::ResourceExhaustion);
}
let entropy = self.raw_entropy();
let mut synapse = Synapse::new();
synapse.set_raw_entropy(entropy);
synapse.set_raw_surprise(0);
synapse.set_has_bias(false);
synapse.set_anchor_hash(0);
Ok(synapse)
}
#[must_use = "ignoring the safety check defeats the purpose of the guard"]
pub fn check_ctrl(&self) -> Result<BodyOutput, KernelError> {
if self.memory_ceiling_bytes == 0 {
return Err(KernelError::ResourceExhaustion);
}
#[cfg(any(test, feature = "testing"))]
let current_rss = if self.pressure_override.is_some() {
self.memory_ceiling_bytes / 2
} else {
match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
}
};
#[cfg(not(any(test, feature = "testing")))]
let current_rss = match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
};
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
if ratio >= 1.0 {
return Err(KernelError::ResourceExhaustion);
}
let pressure_pct = (ratio * 100.0).min(100.0) as u8;
Ok(BodyOutput {
error_body: ratio as f32,
pressure: pressure_pct,
is_exhausted: false,
})
}
#[must_use = "ignoring the safety check defeats the purpose of the guard"]
pub fn check_with_entropy(&self, entropy: u16) -> Result<Synapse, KernelError> {
if self.memory_ceiling_bytes == 0 {
return Err(KernelError::ResourceExhaustion);
}
#[cfg(any(test, feature = "testing"))]
let current_rss = if self.pressure_override.is_some() {
self.memory_ceiling_bytes / 2
} else {
match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
}
};
#[cfg(not(any(test, feature = "testing")))]
let current_rss = match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
};
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
if ratio >= 1.0 {
return Err(KernelError::ResourceExhaustion);
}
let mut synapse = Synapse::new();
synapse.set_raw_entropy(entropy);
synapse.set_raw_surprise(0);
synapse.set_has_bias(false);
synapse.set_anchor_hash(0);
Ok(synapse)
}
#[cfg(feature = "std")]
pub fn check_blocking(&self) -> Result<Synapse, KernelError> {
self.check_blocking_with_max_retries(3)
}
#[cfg(feature = "std")]
pub fn check_blocking_with_max_retries(
&self,
max_retries: u32,
) -> Result<Synapse, KernelError> {
self.check_blocking_with_max_retries_and_policy(
max_retries,
&crate::llmosafe_integration::EscalationPolicy::default(),
)
}
#[cfg(feature = "std")]
pub fn check_blocking_with_max_retries_and_policy(
&self,
max_retries: u32,
policy: &crate::llmosafe_integration::EscalationPolicy,
) -> Result<Synapse, KernelError> {
use crate::llmosafe_integration::{PressureLevel, SafetyDecision};
let mut retries = 0u32;
loop {
if retries >= max_retries {
return Err(KernelError::DeadlineExceeded);
}
let entropy = self.raw_entropy();
let pressure_pct = self.pressure();
let pressure_level = PressureLevel::from_percentage(pressure_pct);
let decision = policy.decide_with_pressure(entropy, 0, false, pressure_level);
match decision {
SafetyDecision::Proceed | SafetyDecision::Warn(_) => {
return self.check_with_entropy(entropy);
}
SafetyDecision::Escalate { cooldown_ms, .. } => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Halt(_, cooldown_ms) => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Exit(err) => {
return Err(err);
}
}
}
}
#[cfg(feature = "std")]
pub fn check_with_deadline(
&self,
deadline: std::time::Instant,
) -> Result<Synapse, KernelError> {
use crate::llmosafe_integration::{EscalationPolicy, PressureLevel, SafetyDecision};
let policy = EscalationPolicy::default();
let mut retries = 0u32;
loop {
if std::time::Instant::now() >= deadline {
return Err(KernelError::DeadlineExceeded);
}
if retries >= 3 {
return Err(KernelError::DeadlineExceeded);
}
let entropy = self.raw_entropy();
let pressure_pct = self.pressure();
let pressure_level = PressureLevel::from_percentage(pressure_pct);
let decision = policy.decide_with_pressure(entropy, 0, false, pressure_level);
match decision {
SafetyDecision::Proceed | SafetyDecision::Warn(_) => {
return self.check_with_entropy(entropy);
}
SafetyDecision::Escalate { cooldown_ms, .. } => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Halt(_, cooldown_ms) => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Exit(err) => {
return Err(err);
}
}
}
}
#[cfg(unix)]
pub fn current_rss_bytes() -> usize {
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
if ret == 0 {
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly",
))]
{
(usage.ru_maxrss as usize).saturating_mul(1024)
}
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly",
)))]
{
usage.ru_maxrss as usize
}
} else {
let rss = Self::read_rss_from_proc();
if rss.is_none() {
tracing::warn!(
target: "llmosafe::body",
"current_rss_bytes(): getrusage failed and /proc/self/status fallback also failed. Returning 0 — this may mean RSS measurement is unavailable, not zero physical memory use"
);
}
rss.unwrap_or(0)
}
}
#[cfg(windows)]
pub fn current_rss_bytes() -> usize {
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::ProcessStatus::{
GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS,
};
let mut counters: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
let handle: HANDLE = unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() };
let ret = unsafe {
GetProcessMemoryInfo(
handle,
&mut counters,
std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
)
};
if ret != 0 {
counters.WorkingSetSize as usize
} else {
0
}
}
#[cfg(not(any(unix, windows)))]
pub fn current_rss_bytes() -> usize {
0
}
#[cfg(target_os = "linux")]
fn try_current_rss_bytes() -> Option<usize> {
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
if ret == 0 {
Some((usage.ru_maxrss as usize).saturating_mul(1024))
} else {
Self::read_rss_from_proc()
}
}
#[cfg(unix)]
#[cfg(not(target_os = "linux"))]
fn try_current_rss_bytes() -> Option<usize> {
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
if ret == 0 {
Some(usage.ru_maxrss as usize)
} else {
None
}
}
#[cfg(windows)]
fn try_current_rss_bytes() -> Option<usize> {
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::ProcessStatus::{
GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS,
};
let mut counters: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
let handle: HANDLE = unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() };
let ret = unsafe {
GetProcessMemoryInfo(
handle,
&mut counters,
std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
)
};
if ret != 0 {
Some(counters.WorkingSetSize as usize)
} else {
None
}
}
#[cfg(not(any(unix, windows)))]
fn try_current_rss_bytes() -> Option<usize> {
None
}
#[cfg(target_os = "linux")]
fn read_rss_from_proc() -> Option<usize> {
let file = match fs::File::open("/proc/self/status") {
Ok(f) => f,
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"Cannot open /proc/self/status for RSS measurement: {}",
e
);
return None;
}
};
for line in BufReader::new(file).lines().map_while(Result::ok) {
if line.starts_with("VmRSS:") {
if let Some(size_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = size_str.parse::<usize>() {
return Some(kb.saturating_mul(1024));
}
}
}
}
tracing::warn!(
target: "llmosafe::body",
"VmRSS line not found or unparseable in /proc/self/status"
);
None
}
#[cfg(not(target_os = "linux"))]
fn read_rss_from_proc() -> Option<usize> {
None
}
#[cfg(target_os = "linux")]
pub fn system_memory_bytes() -> usize {
if let Ok(file) = fs::File::open("/proc/meminfo") {
for line in BufReader::new(file).lines().map_while(Result::ok) {
if line.starts_with("MemTotal:") {
if let Some(size_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = size_str.parse::<usize>() {
return kb.saturating_mul(1024);
}
}
}
}
}
0
}
#[cfg(not(target_os = "linux"))]
pub fn system_memory_bytes() -> usize {
0
}
pub fn auto(fraction: f64) -> Self {
let sys_mem = Self::system_memory_bytes();
let ceiling = if sys_mem > 0 {
(sys_mem as f64 * fraction) as usize
} else {
0
};
Self::new(ceiling)
}
#[cfg(target_os = "linux")]
fn parse_proc_stat() -> Option<(u64, u64)> {
let content = match fs::read_to_string("/proc/stat") {
Ok(c) => c,
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"Cannot read /proc/stat in parse_proc_stat: {}",
e
);
return None;
}
};
let line = match content.lines().next() {
Some(l) => l,
None => {
tracing::warn!(
target: "llmosafe::body",
"parse_proc_stat: no cpu line in /proc/stat"
);
return None;
}
};
let mut parts = line.split_whitespace().skip(1);
let user = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: failed to parse user: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: missing user field in /proc/stat cpu line");
return None;
}
};
let nice = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: failed to parse nice: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: missing nice field in /proc/stat cpu line");
return None;
}
};
let system = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: failed to parse system: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: missing system field in /proc/stat cpu line");
return None;
}
};
let idle = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: failed to parse idle: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: missing idle field in /proc/stat cpu line");
return None;
}
};
let iowait = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: failed to parse iowait: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat: missing iowait field in /proc/stat cpu line");
return None;
}
};
let active = user + nice + system;
let total = active + idle + iowait;
Some((active, total))
}
#[cfg(target_os = "linux")]
fn parse_proc_stat_iowait() -> Option<(u64, u64)> {
let content = match fs::read_to_string("/proc/stat") {
Ok(c) => c,
Err(e) => {
tracing::warn!(
target: "llmosafe::body",
"Cannot read /proc/stat in parse_proc_stat_iowait: {}",
e
);
return None;
}
};
let line = match content.lines().next() {
Some(l) => l,
None => {
tracing::warn!(
target: "llmosafe::body",
"parse_proc_stat_iowait: no cpu line in /proc/stat"
);
return None;
}
};
let mut parts = line.split_whitespace().skip(1);
let user = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: failed to parse user: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: missing user field in /proc/stat cpu line");
return None;
}
};
let nice = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: failed to parse nice: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: missing nice field in /proc/stat cpu line");
return None;
}
};
let system = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: failed to parse system: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: missing system field in /proc/stat cpu line");
return None;
}
};
let idle = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: failed to parse idle: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: missing idle field in /proc/stat cpu line");
return None;
}
};
let iowait = match parts.next() {
Some(s) => match s.parse::<u64>() {
Ok(v) => v,
Err(e) => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: failed to parse iowait: {}", e);
return None;
}
},
None => {
tracing::warn!(target: "llmosafe::body", "parse_proc_stat_iowait: missing iowait field in /proc/stat cpu line");
return None;
}
};
let active = user + nice + system;
let total = active + idle + iowait;
Some((iowait, total))
}
pub fn system_cpu_load() -> u8 {
#[cfg(target_os = "linux")]
{
if let Some((active1, total1)) = Self::parse_proc_stat() {
thread::sleep(Duration::from_millis(100));
if let Some((active2, total2)) = Self::parse_proc_stat() {
let d_active = active2.saturating_sub(active1);
let d_total = total2.saturating_sub(total1);
if d_total == 0 {
return 0;
}
return ((d_active as f64 / d_total as f64) * 100.0) as u8;
}
}
0
}
#[cfg(not(target_os = "linux"))]
{
0
}
}
#[cfg(target_os = "linux")]
fn delta_iowait_ratio() -> f64 {
if let Some((iowait1, total1)) = Self::parse_proc_stat_iowait() {
thread::sleep(Duration::from_millis(100));
if let Some((iowait2, total2)) = Self::parse_proc_stat_iowait() {
let d_iowait = iowait2.saturating_sub(iowait1);
let d_total = total2.saturating_sub(total1);
if d_total == 0 {
return 0.0;
}
return (d_iowait as f64 / d_total as f64).min(1.0);
}
}
0.0
}
}
#[no_mangle]
pub extern "C" fn llmosafe_get_environmental_entropy() -> u16 {
let guard = ResourceGuard::auto(0.5);
guard.raw_entropy()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_cpu_load_returns_bounded_value() {
let load = ResourceGuard::system_cpu_load();
assert!(load <= 100, "CPU load {} should be <= 100", load);
}
#[test]
fn test_system_cpu_load_two_calls_consistent() {
let load1 = ResourceGuard::system_cpu_load();
let load2 = ResourceGuard::system_cpu_load();
assert!(load1 <= 100);
assert!(load2 <= 100);
}
#[cfg(target_os = "linux")]
#[test]
fn test_parse_proc_stat_returns_some() {
let result = ResourceGuard::parse_proc_stat();
assert!(result.is_some(), "/proc/stat should be parseable on Linux");
let (active, total) = result.expect("checked above");
assert!(total >= active, "total must be >= active");
assert!(total > 0, "total should be positive on a running system");
}
#[cfg(target_os = "linux")]
#[test]
fn test_delta_iowait_ratio_bounded() {
let ratio = ResourceGuard::delta_iowait_ratio();
assert!(
(0.0..=1.0).contains(&ratio),
"iowait ratio {} should be in [0.0, 1.0]",
ratio
);
}
#[test]
fn test_check_ctrl_zero_ceiling_returns_exhaustion() {
let guard = ResourceGuard::new(0);
let result = guard.check_ctrl();
assert_eq!(result.unwrap_err(), KernelError::ResourceExhaustion);
}
#[test]
fn test_check_ctrl_valid_ceiling_returns_body_output() {
let guard = ResourceGuard::for_testing(100 * 1024 * 1024 * 1024, 100, 20);
let result = guard.check_ctrl();
match result {
Ok(result) => {
assert!((0.0..=1.0).contains(&result.error_body));
assert!(result.pressure <= 100);
assert!(!result.is_exhausted);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[test]
fn test_check_blocking_returns_deadline_exceeded() {
let guard = ResourceGuard::new(0);
let result = guard.check_blocking();
assert!(result.is_err());
}
#[test]
fn test_capture_vitals_returns_bounded_values() {
let vitals = EnvironmentalVitals::capture();
assert!(vitals.load_avg >= 0.0, "load_avg should be >= 0.0");
assert!(
vitals.load_avg.is_finite(),
"load_avg should be a finite value"
);
if !vitals.vitals_available {
assert_eq!(
vitals.iowait, 0,
"Fail-closed behavior: iowait must be 0 if unavailable"
);
assert_eq!(
vitals.load_avg, 0.0,
"Fail-closed behavior: load_avg must be 0.0 if unavailable"
);
}
}
#[test]
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
fn test_current_rss_bytes_returns_positive() {
let rss = ResourceGuard::current_rss_bytes();
assert!(rss > 0, "current_rss_bytes should be positive");
}
#[test]
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
fn test_current_rss_bytes_returns_zero_on_unsupported() {
let rss = ResourceGuard::current_rss_bytes();
assert_eq!(
rss, 0,
"current_rss_bytes should be 0 on unsupported platforms"
);
}
#[test]
fn test_check_blocking_succeeds_under_no_pressure() {
let guard = ResourceGuard::new(1024 * 1024 * 1024);
let result = guard.check_blocking();
match result {
Ok(synapse) => {
assert!(synapse.raw_entropy() <= 1000);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[test]
fn test_check_blocking_deterministic_proceed_and_warn() {
let guard = ResourceGuard::for_testing(1024, 200, 10);
let result = guard.check_blocking();
assert!(
result.is_ok(),
"Low pressure and low entropy should return Ok"
);
let guard_warn = ResourceGuard::for_testing(1024, 200, 50);
let result_warn = guard_warn.check_blocking();
assert!(
result_warn.is_ok(),
"Elevated pressure with low entropy returns Ok (Warn/Proceed)"
);
}
#[test]
fn test_check_blocking_deterministic_sustained_failure() {
let guard = ResourceGuard::for_testing(1024, 1000, 100);
let result = guard.check_blocking_with_max_retries(0); assert!(
matches!(result.unwrap_err(), KernelError::DeadlineExceeded),
"Sustained failure pressure returns DeadlineExceeded"
);
}
#[test]
fn test_check_with_deadline_succeeds_before_expiration() {
let guard = ResourceGuard::new(1024 * 1024 * 1024); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(1);
let result = guard.check_with_deadline(deadline);
match result {
Ok(synapse) => {
assert!(synapse.raw_entropy() <= 1000);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[test]
fn test_check_with_deadline_fails_after_expiration() {
let guard = ResourceGuard::new(1024 * 1024 * 1024); let deadline = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(1))
.unwrap();
let result = guard.check_with_deadline(deadline);
assert!(
matches!(result.unwrap_err(), KernelError::DeadlineExceeded),
"check_with_deadline should return DeadlineExceeded after deadline"
);
}
#[test]
fn test_check_with_deadline_deterministic_future_deadline_low_pressure() {
let guard = ResourceGuard::for_testing(1024, 100, 10);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(60);
let result = guard.check_with_deadline(deadline);
assert!(
result.is_ok(),
"Future deadline with low pressure and entropy returns Ok"
);
}
#[test]
fn test_check_with_deadline_deterministic_sustained_blocking() {
let guard = ResourceGuard::for_testing(1024, 1000, 100);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(60);
let result = guard.check_with_deadline(deadline);
assert!(
matches!(result.unwrap_err(), KernelError::DeadlineExceeded),
"Sustained blocking condition exits by retry limit instead of spinning forever"
);
}
#[test]
fn test_check_zero_ceiling_returns_exhaustion() {
let guard = ResourceGuard::new(0);
let result = guard.check();
assert_eq!(
result.unwrap_err(),
KernelError::ResourceExhaustion,
"check() with zero ceiling must return ResourceExhaustion"
);
}
#[test]
fn test_check_with_testing_override_returns_valid_synapse() {
let guard = ResourceGuard::for_testing(1024, 200, 10);
let result = guard.check();
assert!(
result.is_ok(),
"check() with testing overrides should succeed"
);
let synapse = result.unwrap();
assert!(
synapse.raw_entropy() > 0,
"raw_entropy should be the override value 200"
);
assert_eq!(
synapse.anchor_hash(),
0,
"anchor_hash should be 0 as set by check()"
);
}
#[test]
fn test_check_high_ceiling_returns_synapse() {
let guard = ResourceGuard::new(1024 * 1024 * 1024);
let result = guard.check();
match result {
Ok(synapse) => {
assert!(
synapse.raw_entropy() > 0,
"raw_entropy from real RSS should be positive"
);
assert_eq!(
synapse.anchor_hash(),
0,
"anchor_hash should be 0 as set by check()"
);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error from check(): {:?}", e),
}
}
#[test]
fn test_check_blocking_escalate_pressure_with_zero_retries() {
let guard = ResourceGuard::for_testing(1024, 400, 52);
let result = guard.check_blocking_with_max_retries(0);
assert!(
matches!(result.unwrap_err(), KernelError::DeadlineExceeded),
"Escalate-producing conditions with max_retries=0 must return DeadlineExceeded"
);
}
#[test]
fn test_check_ctrl_with_testing_override_returns_valid_body_output() {
let guard = ResourceGuard::for_testing(1024, 200, 10);
let result = guard.check_ctrl();
assert!(
result.is_ok(),
"check_ctrl() with testing overrides should return Ok"
);
let output = result.unwrap();
assert!(
(0.0..=1.0).contains(&output.error_body),
"error_body {:.4} must be in [0.0, 1.0]",
output.error_body
);
assert!(
output.pressure <= 100,
"pressure {} must be ≤ 100",
output.pressure
);
assert!(
!output.is_exhausted,
"is_exhausted must be false when ceiling > 0 and ratio < 1.0"
);
}
#[test]
fn test_check_with_entropy_testing_mode() {
let guard = ResourceGuard::for_testing(1024, 100, 10);
let result = guard.check_with_entropy(100);
assert!(
result.is_ok(),
"check_with_entropy should succeed with testing overrides"
);
let synapse = result.unwrap();
assert_eq!(
synapse.raw_entropy(),
100,
"raw_entropy must match the provided entropy argument"
);
assert_eq!(synapse.raw_surprise(), 0, "raw_surprise should be 0");
assert!(!synapse.has_bias(), "has_bias should be false");
assert_eq!(synapse.anchor_hash(), 0, "anchor_hash should be 0");
}
#[test]
fn test_check_with_entropy_zero_ceiling() {
let guard = ResourceGuard::new(0);
let result = guard.check_with_entropy(100);
assert_eq!(
result.unwrap_err(),
KernelError::ResourceExhaustion,
"check_with_entropy with zero ceiling must return ResourceExhaustion"
);
}
}