use std::io::{Error as IoError, Result as IoResult};
use std::mem::{size_of, zeroed};
use libc::{CPU_SET, cpu_set_t, gettid, pid_t, sched_getaffinity, sched_getcpu, sched_setaffinity};
pub(crate) struct CoreAffinityGuard {
thread: pid_t,
old_affinity: cpu_set_t,
}
impl Drop for CoreAffinityGuard {
fn drop(&mut self) {
unsafe { sched_setaffinity(self.thread, size_of::<cpu_set_t>(), &self.old_affinity) };
}
}
fn get_affinity() -> IoResult<cpu_set_t> {
let thread = unsafe { gettid() };
unsafe {
let mut set: cpu_set_t = zeroed();
let code = sched_getaffinity(thread, size_of::<cpu_set_t>(), &mut set);
if code != 0 {
return Err(std::io::Error::from_raw_os_error(code));
}
Ok(set)
}
}
#[must_use]
pub(crate) fn fix_core_affinity() -> IoResult<CoreAffinityGuard> {
let thread = unsafe { gettid() };
let old_affinity = get_affinity()?;
unsafe {
let mut new_set: cpu_set_t = zeroed();
let cpu = sched_getcpu();
CPU_SET(cpu as usize, &mut new_set);
let code = sched_setaffinity(thread, size_of::<cpu_set_t>(), &new_set);
if code != 0 {
return Err(IoError::from_raw_os_error(code));
}
};
Ok(CoreAffinityGuard {
thread,
old_affinity,
})
}
#[cfg(test)]
mod tests {
use std::{collections::HashSet, time::Duration};
use super::*;
#[test]
fn core_pinning() {
let affinity = get_affinity();
let _guard = fix_core_affinity();
let mut set = HashSet::new();
for _ in 0..20 {
std::thread::sleep(Duration::from_millis(10));
set.insert(unsafe { libc::sched_getcpu() });
}
assert_eq!(set.len(), 1);
drop(_guard);
assert_eq!(
format!("{:?}", affinity.unwrap()),
format!("{:?}", get_affinity().unwrap())
);
}
}