hyperi_rustlib/memory/
cgroup.rs1use std::fs;
12
13pub fn detect_memory_limit() -> u64 {
22 if let Some(limit) = read_cgroup_v2_limit() {
24 tracing::info!(
25 limit_bytes = limit,
26 source = "cgroup-v2",
27 "detected memory limit"
28 );
29 return limit;
30 }
31
32 if let Some(limit) = read_cgroup_v1_limit() {
34 tracing::info!(
35 limit_bytes = limit,
36 source = "cgroup-v1",
37 "detected memory limit"
38 );
39 return limit;
40 }
41
42 let mut sys = sysinfo::System::new();
44 sys.refresh_memory();
45 let total = sys.total_memory();
46 tracing::info!(
47 limit_bytes = total,
48 source = "system-memory",
49 "detected memory limit (no cgroup)"
50 );
51 total
52}
53
54#[must_use]
65pub fn detect_memory_pressure() -> Option<f64> {
66 let limit = read_cgroup_v2_limit().or_else(read_cgroup_v1_limit)?;
67 if limit == 0 {
68 return None;
69 }
70 let current = read_cgroup_v2_current().or_else(read_cgroup_v1_current)?;
71 Some(current as f64 / limit as f64)
72}
73
74fn read_cgroup_v2_limit() -> Option<u64> {
75 let content = fs::read_to_string("/sys/fs/cgroup/memory.max").ok()?;
76 let trimmed = content.trim();
77 if trimmed == "max" {
78 return None; }
80 trimmed.parse::<u64>().ok()
81}
82
83fn read_cgroup_v2_current() -> Option<u64> {
84 fs::read_to_string("/sys/fs/cgroup/memory.current")
85 .ok()?
86 .trim()
87 .parse::<u64>()
88 .ok()
89}
90
91fn read_cgroup_v1_current() -> Option<u64> {
92 fs::read_to_string("/sys/fs/cgroup/memory/memory.usage_in_bytes")
93 .ok()?
94 .trim()
95 .parse::<u64>()
96 .ok()
97}
98
99fn read_cgroup_v1_limit() -> Option<u64> {
100 let content = fs::read_to_string("/sys/fs/cgroup/memory/memory.limit_in_bytes").ok()?;
101 let value = content.trim().parse::<u64>().ok()?;
102 if value > 1 << 62 {
104 return None;
105 }
106 Some(value)
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_detect_memory_limit_returns_nonzero() {
115 let limit = detect_memory_limit();
116 assert!(limit > 0, "memory limit should be positive");
117 }
118
119 #[test]
120 fn test_detect_memory_pressure_is_none_or_valid_fraction() {
121 match detect_memory_pressure() {
124 None => {}
125 Some(r) => assert!(
126 r.is_finite() && r >= 0.0,
127 "cgroup pressure must be finite and non-negative, got {r}"
128 ),
129 }
130 }
131}