Skip to main content

hyperi_rustlib/memory/
cgroup.rs

1// Project:   hyperi-rustlib
2// File:      src/memory/cgroup.rs
3// Purpose:   Cgroup-aware memory limit detection
4// Language:  Rust
5//
6// License:   BUSL-1.1
7// Copyright: (c) 2026 HYPERI PTY LIMITED
8
9//! Cgroup-aware memory limit detection.
10
11use std::fs;
12
13/// Detect the memory limit for this process.
14///
15/// Priority:
16/// 1. Cgroup v2: `/sys/fs/cgroup/memory.max`
17/// 2. Cgroup v1: `/sys/fs/cgroup/memory/memory.limit_in_bytes`
18/// 3. System available memory (via sysinfo)
19///
20/// Returns the limit in bytes.
21pub fn detect_memory_limit() -> u64 {
22    // Try cgroup v2
23    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    // Try cgroup v1
33    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    // Fallback to system memory
43    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
54fn read_cgroup_v2_limit() -> Option<u64> {
55    let content = fs::read_to_string("/sys/fs/cgroup/memory.max").ok()?;
56    let trimmed = content.trim();
57    if trimmed == "max" {
58        return None; // No limit set
59    }
60    trimmed.parse::<u64>().ok()
61}
62
63fn read_cgroup_v1_limit() -> Option<u64> {
64    let content = fs::read_to_string("/sys/fs/cgroup/memory/memory.limit_in_bytes").ok()?;
65    let value = content.trim().parse::<u64>().ok()?;
66    // cgroup v1 uses a very large number for "no limit"
67    if value > 1 << 62 {
68        return None;
69    }
70    Some(value)
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_detect_memory_limit_returns_nonzero() {
79        let limit = detect_memory_limit();
80        assert!(limit > 0, "memory limit should be positive");
81    }
82}