use std::io;
#[derive(Debug, thiserror::Error)]
pub enum AffinityError {
#[error("cpu {requested} out of range (cpu_set_t supports ids below {available})")]
OutOfRange { requested: usize, available: usize },
#[error("sched_setaffinity failed for cpu {cpu}: {source}")]
SetFailed {
cpu: usize,
#[source]
source: io::Error,
},
}
impl From<AffinityError> for io::Error {
fn from(e: AffinityError) -> Self {
Self::other(e.to_string())
}
}
pub fn pin_current_thread_to(cpu: usize) -> Result<(), AffinityError> {
let available = libc::CPU_SETSIZE as usize;
if cpu >= available {
return Err(AffinityError::OutOfRange {
requested: cpu,
available,
});
}
let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
unsafe { libc::CPU_SET(cpu, &mut set) };
let rc = unsafe { libc::sched_setaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &set) };
if rc == 0 {
Ok(())
} else {
Err(AffinityError::SetFailed {
cpu,
source: io::Error::last_os_error(),
})
}
}
pub fn unpin_current_thread() -> Result<(), AffinityError> {
let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
for cpu in 0..libc::CPU_SETSIZE as usize {
unsafe { libc::CPU_SET(cpu, &mut set) };
}
let rc = unsafe { libc::sched_setaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &set) };
if rc == 0 {
Ok(())
} else {
Err(AffinityError::SetFailed {
cpu: usize::MAX,
source: io::Error::last_os_error(),
})
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
fn first_allowed_cpu() -> usize {
let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
let rc =
unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) };
assert_eq!(rc, 0, "sched_getaffinity failed");
(0..libc::CPU_SETSIZE as usize)
.find(|&cpu| {
unsafe { libc::CPU_ISSET(cpu, &set) }
})
.expect("current thread must have at least one allowed CPU")
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn pin_to_allowed_cpu_succeeds() {
pin_current_thread_to(first_allowed_cpu()).unwrap();
}
#[test]
fn out_of_range_cpu_returns_error() {
let r = pin_current_thread_to(usize::MAX);
assert!(matches!(r, Err(AffinityError::OutOfRange { .. })));
}
}