use std::time::Instant;
use sysinfo::{Pid as SysPid, ProcessRefreshKind, ProcessesToUpdate, System};
use thiserror::Error;
use crate::sys::Pid;
#[derive(Debug, Error)]
pub enum CpuError {
#[error("process with id `{0}` not found")]
InvalidPid(u32),
}
pub struct CpuUsage {
pid: SysPid,
system: System,
cached_total_usage: Option<f64>,
last_refresh: Instant,
}
pub fn usage(pid: Option<Pid>) -> Result<CpuUsage, CpuError> {
let pid = pid.unwrap_or(std::process::id());
let mut cpu_usage = CpuUsage::new(pid);
cpu_usage.refresh_cached_usage()?;
Ok(cpu_usage)
}
impl CpuUsage {
fn new(pid: Pid) -> Self {
CpuUsage {
pid: SysPid::from_u32(pid),
system: System::new_all(),
cached_total_usage: None,
last_refresh: Instant::now(),
}
}
pub fn poll_total(&mut self) -> Result<f64, CpuError> {
self.refresh_cached_usage()?;
Ok(self.cached_total_usage.unwrap())
}
pub fn poll_per_core(&mut self) -> Result<f64, CpuError> {
let total_usage = self.poll_total()?;
Ok(total_usage / self.system.cpus().len() as f64)
}
fn refresh_cached_usage(&mut self) -> Result<(), CpuError> {
if self.cached_total_usage.is_some()
&& self.last_refresh.elapsed() < sysinfo::MINIMUM_CPU_UPDATE_INTERVAL
{
return Ok(());
}
self.system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[self.pid]),
true,
ProcessRefreshKind::nothing().with_cpu(),
);
let Some(process) = self.system.process(self.pid) else {
return Err(CpuError::InvalidPid(self.pid.as_u32()));
};
self.cached_total_usage = Some(process.cpu_usage() as f64 / 100.0);
self.last_refresh = Instant::now();
Ok(())
}
}