ruvector_memopt/windows/
safety.rs

1//! Safety mechanisms to prevent system instability
2
3use std::time::{Duration, Instant};
4use tracing::{info, warn};
5
6/// Safety configuration
7#[derive(Debug, Clone)]
8pub struct SafetyConfig {
9    /// Minimum available memory to maintain (MB)
10    pub min_available_mb: f64,
11    /// Maximum optimization frequency
12    pub min_interval: Duration,
13    /// Maximum processes to trim per optimization
14    pub max_processes_per_run: usize,
15    /// Enable dry-run mode (no actual changes)
16    pub dry_run: bool,
17    /// Protected process names (case-insensitive)
18    pub protected_processes: Vec<String>,
19}
20
21impl Default for SafetyConfig {
22    fn default() -> Self {
23        Self {
24            min_available_mb: 1024.0, // Keep at least 1GB free
25            min_interval: Duration::from_secs(30),
26            max_processes_per_run: 50,
27            dry_run: false,
28            protected_processes: vec![
29                // Windows critical
30                "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                // Security
40                "msmpeng.exe".into(), // Windows Defender
41                "securityhealthservice.exe".into(),
42                // Anti-virus common
43                "avgnt.exe".into(),
44                "avp.exe".into(),
45            ],
46        }
47    }
48}
49
50/// Safety guard for memory optimization
51pub 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    /// Check if optimization is safe to proceed
69    pub fn check_safe(&self, current_available_mb: f64) -> Result<(), String> {
70        // Check memory floor
71        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        // Check rate limit
79        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        // Check consecutive failures
90        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    /// Check if a process is protected
101    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    /// Record optimization attempt
108    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    /// Check if dry-run mode
121    pub fn is_dry_run(&self) -> bool {
122        self.config.dry_run
123    }
124    
125    /// Get max processes per run
126    pub fn max_processes(&self) -> usize {
127        self.config.max_processes_per_run
128    }
129    
130    /// Emergency stop - disable further optimizations
131    pub fn emergency_stop(&mut self) {
132        self.consecutive_failures = 100; // Triggers safety check failure
133        warn!("Emergency stop activated - optimizations disabled");
134    }
135    
136    /// Reset safety counters
137    pub fn reset(&mut self) {
138        self.consecutive_failures = 0;
139        self.last_optimization = None;
140        info!("Safety counters reset");
141    }
142    
143    /// Get statistics
144    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}