use std::ops;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use heim_common::prelude::*;
use heim_common::units::{frequency, Frequency};
use heim_runtime::fs;
#[derive(Debug, Default)]
pub struct CpuFrequency {
current: Frequency,
min: Option<Frequency>,
max: Option<Frequency>,
}
impl CpuFrequency {
pub fn current(&self) -> Frequency {
self.current
}
pub fn min(&self) -> Option<Frequency> {
self.min
}
pub fn max(&self) -> Option<Frequency> {
self.max
}
}
impl ops::Add<CpuFrequency> for CpuFrequency {
type Output = CpuFrequency;
fn add(self, rhs: CpuFrequency) -> CpuFrequency {
let current = self.current + rhs.current;
let min = match (self.min, rhs.min) {
(Some(left), Some(right)) => Some(left + right),
(Some(left), None) => Some(left),
(None, Some(right)) => Some(right),
(None, None) => None,
};
let max = match (self.max, rhs.max) {
(Some(left), Some(right)) => Some(left + right),
(Some(left), None) => Some(left),
(None, Some(right)) => Some(right),
(None, None) => None,
};
CpuFrequency { current, max, min }
}
}
pub fn frequency() -> impl Future<Output = Result<CpuFrequency>> {
let init = CpuFrequency::default();
frequencies()
.try_fold((init, 0u64), |(acc, amount), freq| {
future::ok((acc + freq, amount + 1))
})
.then(|result| {
match result {
Ok((ref freq, amount)) if amount > 0 => future::ok(CpuFrequency {
current: freq.current / amount,
min: freq.min.map(|value| value / amount),
max: freq.max.map(|value| value / amount),
}),
Ok(_) => future::err(Error::incompatible(
"No CPU frequencies was found, running in VM?",
)),
Err(e) => future::err(e),
}
})
}
pub fn frequencies() -> impl Stream<Item = Result<CpuFrequency>> {
fs::read_dir("/sys/devices/system/cpu/")
.map_err(Error::from)
.try_filter(|entry| {
let name = entry.file_name();
let bytes = name.as_bytes();
if !bytes.starts_with(b"cpu") {
return future::ready(false);
}
let all_digits = &bytes[3..].iter().all(|byte| *byte >= b'0' && *byte <= b'9');
future::ready(*all_digits)
})
.map_ok(|entry| entry.path().join("cpufreq"))
.try_filter(|path| {
fs::path_exists(path.clone())
})
.and_then(|path| {
let current = current_freq(&path);
let max = max_freq(&path);
let min = min_freq(&path);
future::try_join3(current, max, min)
})
.and_then(|(current, max, min)| future::ok(CpuFrequency { current, max, min }))
}
#[allow(clippy::redundant_closure)]
fn read_freq(path: PathBuf) -> impl Future<Output = Result<Frequency>> {
fs::read_to_string(path)
.map_err(Error::from)
.and_then(|value| future::ready(value.trim_end().parse::<u64>().map_err(Error::from)))
.map_ok(Frequency::new::<frequency::kilohertz>)
}
fn current_freq(path: &Path) -> impl Future<Output = Result<Frequency>> {
read_freq(path.join("scaling_cur_freq"))
}
fn max_freq(path: &Path) -> impl Future<Output = Result<Option<Frequency>>> {
read_freq(path.join("scaling_max_freq"))
.into_future()
.map(|value| Ok(value.ok()))
}
fn min_freq(path: &Path) -> impl Future<Output = Result<Option<Frequency>>> {
read_freq(path.join("scaling_min_freq"))
.into_future()
.map(|value| Ok(value.ok()))
}