superfluid_daemon/
pressure.rs1use std::sync::atomic::{AtomicU8, Ordering};
4use std::sync::Arc;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum PressureLevel {
8 Normal,
9 Warning,
10 Critical,
11}
12
13pub trait PressureSource {
14 fn level(&mut self) -> PressureLevel;
15}
16
17#[derive(Clone, Default)]
18pub struct ManualPressure {
19 level: Arc<AtomicU8>,
20}
21
22impl ManualPressure {
23 pub fn new() -> ManualPressure {
24 ManualPressure::default()
25 }
26 pub fn set(&self, level: PressureLevel) {
27 self.level.store(
28 match level {
29 PressureLevel::Normal => 0,
30 PressureLevel::Warning => 1,
31 PressureLevel::Critical => 2,
32 },
33 Ordering::Release,
34 );
35 }
36}
37
38impl PressureSource for ManualPressure {
39 fn level(&mut self) -> PressureLevel {
40 match self.level.load(Ordering::Acquire) {
41 0 => PressureLevel::Normal,
42 1 => PressureLevel::Warning,
43 _ => PressureLevel::Critical,
44 }
45 }
46}
47
48#[cfg(target_os = "macos")]
49pub struct MacOsPressure;
50
51#[cfg(target_os = "macos")]
52impl PressureSource for MacOsPressure {
53 fn level(&mut self) -> PressureLevel {
54 let name = c"kern.memorystatus_vm_pressure_level";
55 let mut value: i32 = 0;
56 let mut len = std::mem::size_of::<i32>();
57 let rc = unsafe {
60 libc::sysctlbyname(
61 name.as_ptr(),
62 &mut value as *mut i32 as *mut libc::c_void,
63 &mut len,
64 std::ptr::null_mut(),
65 0,
66 )
67 };
68 if rc != 0 {
69 return PressureLevel::Normal;
70 }
71 match value {
72 4 => PressureLevel::Critical,
73 2 => PressureLevel::Warning,
74 _ => PressureLevel::Normal,
75 }
76 }
77}
78
79#[cfg(target_os = "linux")]
80pub struct LinuxPsiPressure {
81 pub some_warning_pct: f32,
82 pub full_critical_pct: f32,
83}
84
85#[cfg(target_os = "linux")]
86impl Default for LinuxPsiPressure {
87 fn default() -> Self {
88 LinuxPsiPressure {
89 some_warning_pct: 10.0,
90 full_critical_pct: 5.0,
91 }
92 }
93}
94
95#[cfg(target_os = "linux")]
96impl PressureSource for LinuxPsiPressure {
97 fn level(&mut self) -> PressureLevel {
98 let Ok(text) = std::fs::read_to_string("/proc/pressure/memory") else {
99 return PressureLevel::Normal;
100 };
101 let avg10 = |line: &str| -> f32 {
102 line.split_whitespace()
103 .find_map(|kv| kv.strip_prefix("avg10="))
104 .and_then(|v| v.parse().ok())
105 .unwrap_or(0.0)
106 };
107 let mut level = PressureLevel::Normal;
108 for line in text.lines() {
109 if line.starts_with("full") && avg10(line) >= self.full_critical_pct {
110 return PressureLevel::Critical;
111 }
112 if line.starts_with("some") && avg10(line) >= self.some_warning_pct {
113 level = PressureLevel::Warning;
114 }
115 }
116 level
117 }
118}
119
120pub fn os_default() -> Option<Box<dyn PressureSource + Send>> {
121 #[cfg(target_os = "macos")]
122 {
123 Some(Box::new(MacOsPressure))
124 }
125 #[cfg(target_os = "linux")]
126 {
127 Some(Box::new(LinuxPsiPressure::default()))
128 }
129 #[cfg(not(any(target_os = "macos", target_os = "linux")))]
130 {
131 None
132 }
133}
134
135pub enum PressureConfig {
136 None,
137 Os,
138 Manual(ManualPressure),
139}
140
141impl PressureConfig {
142 pub(crate) fn take_source(&mut self) -> Option<Box<dyn PressureSource + Send>> {
143 match self {
144 PressureConfig::None => None,
145 PressureConfig::Os => os_default(),
146 PressureConfig::Manual(m) => Some(Box::new(m.clone())),
147 }
148 }
149}
150
151impl std::fmt::Debug for PressureConfig {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 match self {
154 PressureConfig::None => f.write_str("None"),
155 PressureConfig::Os => f.write_str("Os"),
156 PressureConfig::Manual(_) => f.write_str("Manual"),
157 }
158 }
159}
160
161impl Clone for PressureConfig {
162 fn clone(&self) -> Self {
163 match self {
164 PressureConfig::None => PressureConfig::None,
165 PressureConfig::Os => PressureConfig::Os,
166 PressureConfig::Manual(m) => PressureConfig::Manual(m.clone()),
167 }
168 }
169}