ruvector_memopt/windows/
memory.rs

1//! Windows Memory Management Core with Real Win32 APIs
2
3use sysinfo::System;
4use tracing::{info, warn};
5use std::time::Instant;
6
7#[derive(Debug, Clone)]
8pub struct MemoryStatus {
9    pub total_physical_mb: f64,
10    pub available_physical_mb: f64,
11    pub memory_load_percent: u32,
12    pub total_page_file_mb: f64,
13    pub available_page_file_mb: f64,
14    pub total_virtual_mb: f64,
15    pub available_virtual_mb: f64,
16}
17
18impl MemoryStatus {
19    pub fn used_physical_mb(&self) -> f64 { self.total_physical_mb - self.available_physical_mb }
20    pub fn is_high_pressure(&self) -> bool { self.memory_load_percent > 80 }
21    pub fn is_critical(&self) -> bool { self.memory_load_percent > 95 }
22}
23
24#[derive(Debug, Clone)]
25pub struct OptimizationResult {
26    pub freed_mb: f64,
27    pub before_available_mb: f64,
28    pub after_available_mb: f64,
29    pub processes_trimmed: usize,
30    pub duration_ms: u64,
31}
32
33pub struct WindowsMemoryOptimizer {
34    has_admin: bool,
35}
36
37impl WindowsMemoryOptimizer {
38    pub fn new() -> Self {
39        let has_admin = Self::check_admin();
40        if !has_admin { warn!("Running without admin - limited optimization"); }
41        else { info!("Running with admin privileges - full optimization available"); }
42        Self { has_admin }
43    }
44
45    fn check_admin() -> bool {
46        #[cfg(windows)]
47        {
48            std::process::Command::new("net").args(["session"])
49                .stdout(std::process::Stdio::null())
50                .stderr(std::process::Stdio::null())
51                .status().map(|s| s.success()).unwrap_or(false)
52        }
53        #[cfg(not(windows))]
54        { false }
55    }
56
57    pub fn get_memory_status() -> Result<MemoryStatus, String> {
58        let mut sys = System::new();
59        sys.refresh_memory();
60        let total = sys.total_memory() as f64 / 1024.0 / 1024.0;
61        let avail = sys.available_memory() as f64 / 1024.0 / 1024.0;
62        let load = if total > 0.0 { (((total - avail) / total) * 100.0) as u32 } else { 0 };
63        Ok(MemoryStatus {
64            total_physical_mb: total, available_physical_mb: avail, memory_load_percent: load,
65            total_page_file_mb: total * 1.5, available_page_file_mb: avail,
66            total_virtual_mb: total * 2.0, available_virtual_mb: avail * 2.0,
67        })
68    }
69
70    #[cfg(windows)]
71    pub fn trim_process_working_set(pid: u32) -> Result<u64, String> {
72        use windows::Win32::Foundation::CloseHandle;
73        use windows::Win32::System::Threading::{OpenProcess, PROCESS_SET_QUOTA, PROCESS_QUERY_INFORMATION};
74        use windows::Win32::System::Memory::SetProcessWorkingSetSizeEx;
75        use windows::Win32::System::ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
76        use std::mem::size_of;
77
78        unsafe {
79            let handle = match OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, false, pid) {
80                Ok(h) => h,
81                Err(_) => return Ok(0),
82            };
83
84            let mut mem_counters = PROCESS_MEMORY_COUNTERS::default();
85            mem_counters.cb = size_of::<PROCESS_MEMORY_COUNTERS>() as u32;
86            let before_ws = if GetProcessMemoryInfo(handle, &mut mem_counters, size_of::<PROCESS_MEMORY_COUNTERS>() as u32).is_ok() {
87                mem_counters.WorkingSetSize
88            } else { 0 };
89
90            let _ = SetProcessWorkingSetSizeEx(handle, usize::MAX, usize::MAX, 
91                windows::Win32::System::Memory::SETPROCESSWORKINGSETSIZEEX_FLAGS(0));
92
93            let after_ws = if GetProcessMemoryInfo(handle, &mut mem_counters, size_of::<PROCESS_MEMORY_COUNTERS>() as u32).is_ok() {
94                mem_counters.WorkingSetSize
95            } else { before_ws };
96
97            let _ = CloseHandle(handle);
98            Ok(before_ws.saturating_sub(after_ws) as u64)
99        }
100    }
101
102    #[cfg(not(windows))]
103    pub fn trim_process_working_set(_pid: u32) -> Result<u64, String> { Ok(0) }
104
105    pub fn optimize(&self, aggressive: bool) -> Result<OptimizationResult, String> {
106        let start = Instant::now();
107        let before = Self::get_memory_status()?;
108        let mut trimmed = 0usize;
109        let mut total_freed: u64 = 0;
110
111        if let Ok(procs) = super::process::list_processes() {
112            for pid in procs.iter().take(150) {
113                match Self::trim_process_working_set(*pid) {
114                    Ok(freed) => {
115                        if freed > 0 {
116                            total_freed += freed;
117                            trimmed += 1;
118                        }
119                    }
120                    Err(_) => {}
121                }
122            }
123        }
124
125        // Force garbage collection pause
126        std::thread::sleep(std::time::Duration::from_millis(100));
127
128        let after = Self::get_memory_status()?;
129        let measured_freed = after.available_physical_mb - before.available_physical_mb;
130        let calculated_freed = total_freed as f64 / 1024.0 / 1024.0;
131        let freed_mb = measured_freed.max(calculated_freed).max(0.0);
132
133        info!("Optimized: trimmed {} processes, freed {:.1} MB in {}ms", 
134            trimmed, freed_mb, start.elapsed().as_millis());
135
136        Ok(OptimizationResult {
137            freed_mb,
138            before_available_mb: before.available_physical_mb,
139            after_available_mb: after.available_physical_mb,
140            processes_trimmed: trimmed,
141            duration_ms: start.elapsed().as_millis() as u64,
142        })
143    }
144
145    pub fn has_admin_privileges(&self) -> bool { self.has_admin }
146}
147
148impl Default for WindowsMemoryOptimizer { fn default() -> Self { Self::new() } }