trueno/monitor/
gpu_monitor.rs1use std::collections::VecDeque;
4use std::sync::{Arc, Mutex, RwLock};
5
6use super::{GpuDeviceInfo, GpuMemoryMetrics, GpuMetrics, MonitorConfig, MonitorError};
7
8pub struct GpuMonitor {
30 device_info: GpuDeviceInfo,
32 config: MonitorConfig,
34 history: Arc<RwLock<VecDeque<GpuMetrics>>>,
36 #[cfg(feature = "gpu")]
38 _background_handle: Option<std::thread::JoinHandle<()>>,
39 stop_signal: Arc<Mutex<bool>>,
41}
42
43impl GpuMonitor {
44 #[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
50 pub fn new(device_index: u32, config: MonitorConfig) -> Result<Self, MonitorError> {
51 let device_info = GpuDeviceInfo::query_device(device_index)?;
52 let history = Arc::new(RwLock::new(VecDeque::with_capacity(config.history_size)));
53 let stop_signal = Arc::new(Mutex::new(false));
54
55 let monitor = Self { device_info, config, history, _background_handle: None, stop_signal };
56
57 Ok(monitor)
58 }
59
60 #[cfg(not(feature = "gpu"))]
62 pub fn new(_device_index: u32, _config: MonitorConfig) -> Result<Self, MonitorError> {
63 Err(MonitorError::NotAvailable("GPU feature not enabled".to_string()))
64 }
65
66 #[must_use]
68 pub fn mock(device_info: GpuDeviceInfo, config: MonitorConfig) -> Self {
69 Self {
70 device_info,
71 config,
72 history: Arc::new(RwLock::new(VecDeque::with_capacity(16))),
73 #[cfg(feature = "gpu")]
74 _background_handle: None,
75 stop_signal: Arc::new(Mutex::new(false)),
76 }
77 }
78
79 #[must_use]
81 pub fn device_info(&self) -> &GpuDeviceInfo {
82 &self.device_info
83 }
84
85 #[must_use]
87 pub fn config(&self) -> &MonitorConfig {
88 &self.config
89 }
90
91 pub fn collect(&self) -> Result<GpuMetrics, MonitorError> {
95 let memory = GpuMemoryMetrics::new(
98 self.device_info.vram_total,
99 0, self.device_info.vram_total,
101 );
102
103 let metrics = GpuMetrics::new(self.device_info.index, memory);
104
105 if let Ok(mut history) = self.history.write() {
107 if history.len() >= self.config.history_size {
108 history.pop_front();
109 }
110 history.push_back(metrics.clone());
111 }
112
113 Ok(metrics)
114 }
115
116 pub fn latest(&self) -> Result<GpuMetrics, MonitorError> {
118 self.history
119 .read()
120 .ok()
121 .and_then(|h| h.back().cloned())
122 .ok_or(MonitorError::QueryFailed("No metrics available".to_string()))
123 }
124
125 #[must_use]
127 pub fn history(&self) -> Vec<GpuMetrics> {
128 self.history.read().map(|h| h.iter().cloned().collect()).unwrap_or_default()
129 }
130
131 #[must_use]
133 pub fn sample_count(&self) -> usize {
134 self.history.read().map(|h| h.len()).unwrap_or(0)
135 }
136
137 pub fn clear_history(&self) {
139 if let Ok(mut history) = self.history.write() {
140 history.clear();
141 }
142 }
143
144 #[must_use]
146 pub fn is_collecting(&self) -> bool {
147 #[cfg(feature = "gpu")]
148 {
149 self._background_handle.is_some()
150 }
151 #[cfg(not(feature = "gpu"))]
152 {
153 false
154 }
155 }
156
157 pub fn stop(&self) {
159 if let Ok(mut stop) = self.stop_signal.lock() {
160 *stop = true;
161 }
162 }
163}
164
165impl Drop for GpuMonitor {
166 fn drop(&mut self) {
167 self.stop();
168 }
169}