use std::{io, mem, ops::Deref};
const CPU_SETSIZE: usize = libc::CPU_SETSIZE as usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CpuId(usize);
impl CpuId {
pub fn new(cpu: usize) -> io::Result<Self> {
if cpu < CPU_SETSIZE {
Ok(Self(cpu))
} else {
Err(io::Error::from_raw_os_error(libc::EINVAL))
}
}
}
impl Deref for CpuId {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub fn set_cpu_affinity(
thread_id: Option<libc::pid_t>,
cpus: impl IntoIterator<Item = CpuId>,
) -> io::Result<()> {
let mut cpu_set: libc::cpu_set_t = unsafe { mem::zeroed() };
for cpu in cpus {
unsafe { libc::CPU_SET(*cpu, &mut cpu_set) };
}
let tid = thread_id.unwrap_or(0);
let result =
unsafe { libc::sched_setaffinity(tid, mem::size_of::<libc::cpu_set_t>(), &cpu_set) };
if result != 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn cpu_affinity(thread_id: Option<libc::pid_t>) -> io::Result<Vec<CpuId>> {
let mut cpu_set: libc::cpu_set_t = unsafe { mem::zeroed() };
let tid = thread_id.unwrap_or(0);
let result =
unsafe { libc::sched_getaffinity(tid, mem::size_of::<libc::cpu_set_t>(), &mut cpu_set) };
if result != 0 {
return Err(io::Error::last_os_error());
}
let mut cpus = Vec::new();
for cpu in 0..CPU_SETSIZE {
if unsafe { libc::CPU_ISSET(cpu, &cpu_set) } {
cpus.push(CpuId::new(cpu)?);
}
}
Ok(cpus)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_id_validation() {
let result = CpuId::new(CPU_SETSIZE);
assert_eq!(result.unwrap_err().raw_os_error(), Some(libc::EINVAL));
}
#[test]
fn test_cpu_affinity_returns_sorted() {
let cpus = cpu_affinity(None).expect("failed to query current CPU affinity");
assert!(
cpus.windows(2).all(|window| *window[0] <= *window[1]),
"cpu_affinity should return sorted CPU list"
);
}
}