diff --git a/socktop_agent/src/metrics.rs b/socktop_agent/src/metrics.rs index 52b1858..e1e8c4e 100644 --- a/socktop_agent/src/metrics.rs +++ b/socktop_agent/src/metrics.rs @@ -153,20 +153,41 @@ pub async fn collect_fast_metrics(state: &AppState) -> Metrics { .collect() }; - // GPUs: refresh only when cache is stale - let gpus = if cached_gpus().is_some() { - cached_gpus() - } else if gpu_enabled() { - let v = match collect_all_gpus() { - Ok(v) if !v.is_empty() => Some(v), - Ok(_) => None, - Err(e) => { - warn!("gpu collection failed: {e}"); - None + // GPUs: if we already determined none exist, short-circuit (no repeated probing) + let gpus = if gpu_enabled() { + if state.gpu_checked.load(std::sync::atomic::Ordering::Acquire) + && !state.gpu_present.load(std::sync::atomic::Ordering::Relaxed) + { + None + } else if cached_gpus().is_some() { + cached_gpus() + } else { + let v = match collect_all_gpus() { + Ok(v) if !v.is_empty() => Some(v), + Ok(_) => None, + Err(e) => { + warn!("gpu collection failed: {e}"); + None + } + }; + // First probe records presence; subsequent calls rely on cache flags. + if !state + .gpu_checked + .swap(true, std::sync::atomic::Ordering::AcqRel) + { + if v.is_some() { + state + .gpu_present + .store(true, std::sync::atomic::Ordering::Release); + } else { + state + .gpu_present + .store(false, std::sync::atomic::Ordering::Release); + } } - }; - set_gpus(v.clone()); - v + set_gpus(v.clone()); + v + } } else { None }; diff --git a/socktop_agent/src/state.rs b/socktop_agent/src/state.rs index 53edd32..9b8a6ce 100644 --- a/socktop_agent/src/state.rs +++ b/socktop_agent/src/state.rs @@ -2,7 +2,7 @@ #[cfg(target_os = "linux")] use std::collections::HashMap; -use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; use sysinfo::{Components, Disks, Networks, System}; use tokio::sync::Mutex; @@ -34,6 +34,9 @@ pub struct AppState { pub client_count: Arc, pub auth_token: Option, + // GPU negative cache (probe once). gpu_checked=true after first attempt; gpu_present reflects result. + pub gpu_checked: Arc, + pub gpu_present: Arc, } impl AppState { @@ -54,6 +57,8 @@ impl AppState { auth_token: std::env::var("SOCKTOP_TOKEN") .ok() .filter(|s| !s.is_empty()), + gpu_checked: Arc::new(AtomicBool::new(false)), + gpu_present: Arc::new(AtomicBool::new(false)), } } }