ruvector_memopt/windows/
safety.rs1use std::time::{Duration, Instant};
4use tracing::{info, warn};
5
6#[derive(Debug, Clone)]
8pub struct SafetyConfig {
9 pub min_available_mb: f64,
11 pub min_interval: Duration,
13 pub max_processes_per_run: usize,
15 pub dry_run: bool,
17 pub protected_processes: Vec<String>,
19}
20
21impl Default for SafetyConfig {
22 fn default() -> Self {
23 Self {
24 min_available_mb: 1024.0, min_interval: Duration::from_secs(30),
26 max_processes_per_run: 50,
27 dry_run: false,
28 protected_processes: vec![
29 "system".into(),
31 "csrss.exe".into(),
32 "smss.exe".into(),
33 "lsass.exe".into(),
34 "services.exe".into(),
35 "wininit.exe".into(),
36 "winlogon.exe".into(),
37 "dwm.exe".into(),
38 "explorer.exe".into(),
39 "msmpeng.exe".into(), "securityhealthservice.exe".into(),
42 "avgnt.exe".into(),
44 "avp.exe".into(),
45 ],
46 }
47 }
48}
49
50pub struct SafetyGuard {
52 config: SafetyConfig,
53 last_optimization: Option<Instant>,
54 consecutive_failures: usize,
55 total_optimizations: usize,
56}
57
58impl SafetyGuard {
59 pub fn new(config: SafetyConfig) -> Self {
60 Self {
61 config,
62 last_optimization: None,
63 consecutive_failures: 0,
64 total_optimizations: 0,
65 }
66 }
67
68 pub fn check_safe(&self, current_available_mb: f64) -> Result<(), String> {
70 if current_available_mb < self.config.min_available_mb {
72 return Err(format!(
73 "Available memory ({:.0}MB) below safety floor ({:.0}MB)",
74 current_available_mb, self.config.min_available_mb
75 ));
76 }
77
78 if let Some(last) = self.last_optimization {
80 let elapsed = last.elapsed();
81 if elapsed < self.config.min_interval {
82 return Err(format!(
83 "Rate limited: {:?} remaining",
84 self.config.min_interval - elapsed
85 ));
86 }
87 }
88
89 if self.consecutive_failures >= 3 {
91 return Err(format!(
92 "Too many consecutive failures ({}). Manual intervention needed.",
93 self.consecutive_failures
94 ));
95 }
96
97 Ok(())
98 }
99
100 pub fn is_protected(&self, process_name: &str) -> bool {
102 let name_lower = process_name.to_lowercase();
103 self.config.protected_processes.iter()
104 .any(|p| name_lower.contains(&p.to_lowercase()))
105 }
106
107 pub fn record_attempt(&mut self, success: bool) {
109 self.last_optimization = Some(Instant::now());
110 self.total_optimizations += 1;
111
112 if success {
113 self.consecutive_failures = 0;
114 } else {
115 self.consecutive_failures += 1;
116 warn!("Optimization failed. Consecutive failures: {}", self.consecutive_failures);
117 }
118 }
119
120 pub fn is_dry_run(&self) -> bool {
122 self.config.dry_run
123 }
124
125 pub fn max_processes(&self) -> usize {
127 self.config.max_processes_per_run
128 }
129
130 pub fn emergency_stop(&mut self) {
132 self.consecutive_failures = 100; warn!("Emergency stop activated - optimizations disabled");
134 }
135
136 pub fn reset(&mut self) {
138 self.consecutive_failures = 0;
139 self.last_optimization = None;
140 info!("Safety counters reset");
141 }
142
143 pub fn stats(&self) -> SafetyStats {
145 SafetyStats {
146 total_optimizations: self.total_optimizations,
147 consecutive_failures: self.consecutive_failures,
148 is_healthy: self.consecutive_failures < 3,
149 }
150 }
151}
152
153#[derive(Debug, Clone)]
154pub struct SafetyStats {
155 pub total_optimizations: usize,
156 pub consecutive_failures: usize,
157 pub is_healthy: bool,
158}