M12.8: the app no longer freezes every 10 s on Windows: the system memory is read in the background

Every 10 seconds the UI thread re-read the system's memory for the cache budgets, and on
Windows that starts PowerShell (`Get-CimInstance Win32_OperatingSystem`) and waits for it:
about a second, sometimes two, during which the whole app froze. The reading now runs on a
background thread (`sysinfo::MemoryWatch`); `Session::memory_tick` takes the last reading and
starts the next one, never waiting. Console programs started for it no longer open a console
window (`CREATE_NO_WINDOW`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic
2026-10-05 07:05:17 +07:00
co-authored by Claude Opus 5.5
parent 4b37443481
commit 2d4a0902ea
4 changed files with 77 additions and 5 deletions
+47 -2
View File
@@ -4,7 +4,11 @@
//! Pure Rust and dependency-free: the values come from the operating system's own reporting
//! (`/proc/meminfo` on Linux, `sysctl`/`vm_stat` on macOS, `wmic`/PowerShell on Windows). Every
//! query may fail (sandboxes, the web): callers treat `None` as "unknown" and keep their
//! configured budgets.
//! configured budgets. The periodic reading behind the cache budgets runs on a background
//! thread ([`MemoryWatch`]): on Windows it starts PowerShell, which takes about a second.
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
use serde::Serialize;
@@ -23,10 +27,51 @@ impl SysMemory {
}
}
/// The system's memory, read in the background: [`MemoryWatch::poll`] starts a reading (unless
/// one is running) and [`MemoryWatch::latest`] returns the last one without waiting.
#[derive(Clone, Default)]
pub struct MemoryWatch {
latest: Arc<Mutex<Option<SysMemory>>>,
busy: Arc<AtomicBool>,
}
impl MemoryWatch {
/// Start a new reading in the background (no threads on the web: read inline, it is
/// unknown there anyway).
pub fn poll(&self) {
if self.busy.swap(true, Ordering::AcqRel) {
return;
}
let (latest, busy) = (self.latest.clone(), self.busy.clone());
let read = move || {
if let Some(m) = memory() {
*latest.lock().unwrap_or_else(PoisonError::into_inner) = Some(m);
}
busy.store(false, Ordering::Release);
};
#[cfg(not(target_arch = "wasm32"))]
if std::thread::Builder::new().name("ec-memory".into()).spawn(read).is_err() {
self.busy.store(false, Ordering::Release);
}
#[cfg(target_arch = "wasm32")]
read();
}
/// The last reading (`None` until one finished).
pub fn latest(&self) -> Option<SysMemory> {
*self.latest.lock().unwrap_or_else(PoisonError::into_inner)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[allow(dead_code)]
fn run(cmd: &str, args: &[&str]) -> Option<String> {
let out = std::process::Command::new(cmd).args(args).output().ok()?;
let mut c = std::process::Command::new(cmd);
c.args(args);
// A console program started from the windowed app would flash a console window.
#[cfg(target_os = "windows")]
std::os::windows::process::CommandExt::creation_flags(&mut c, 0x0800_0000); // CREATE_NO_WINDOW
let out = c.output().ok()?;
out.status.success().then(|| String::from_utf8_lossy(&out.stdout).to_string())
}