#![allow(unsafe_code)]
use std::io;
#[derive(Debug, thiserror::Error)]
pub enum AffinityError {
#[error("sched_setaffinity failed: {0}")]
SetFailed(io::Error),
#[error("CPU {cpu} out of range (online: {online})")]
CpuOutOfRange {
cpu: usize,
online: usize,
},
}
#[cfg(target_os = "linux")]
pub fn set_thread_affinity(cpu: usize) -> Result<(), AffinityError> {
let cpu_set_size = std::mem::size_of::<libc::cpu_set_t>() * 8;
if cpu >= cpu_set_size {
let online = online_cpu_count();
return Err(AffinityError::CpuOutOfRange { cpu, online });
}
unsafe {
let mut cpuset: libc::cpu_set_t = std::mem::zeroed();
libc::CPU_ZERO(&mut cpuset);
libc::CPU_SET(cpu, &mut cpuset);
let tid = libc::syscall(libc::SYS_gettid);
let ret = libc::sched_setaffinity(
tid as libc::pid_t,
std::mem::size_of::<libc::cpu_set_t>(),
&cpuset,
);
if ret != 0 {
return Err(AffinityError::SetFailed(io::Error::last_os_error()));
}
}
Ok(())
}
#[cfg(not(target_os = "linux"))]
pub fn set_thread_affinity(_cpu: usize) -> Result<(), AffinityError> {
Ok(())
}
#[cfg(target_os = "linux")]
pub fn set_thread_affinity_range(cpus: &[usize]) -> Result<(), AffinityError> {
let cpu_set_size = std::mem::size_of::<libc::cpu_set_t>() * 8;
for &cpu in cpus {
if cpu >= cpu_set_size {
let online = online_cpu_count();
return Err(AffinityError::CpuOutOfRange { cpu, online });
}
}
unsafe {
let mut cpuset: libc::cpu_set_t = std::mem::zeroed();
libc::CPU_ZERO(&mut cpuset);
for &cpu in cpus {
libc::CPU_SET(cpu, &mut cpuset);
}
let tid = libc::syscall(libc::SYS_gettid);
let ret = libc::sched_setaffinity(
tid as libc::pid_t,
std::mem::size_of::<libc::cpu_set_t>(),
&cpuset,
);
if ret != 0 {
return Err(AffinityError::SetFailed(io::Error::last_os_error()));
}
}
Ok(())
}
#[cfg(not(target_os = "linux"))]
pub fn set_thread_affinity_range(_cpus: &[usize]) -> Result<(), AffinityError> {
Ok(())
}
#[cfg(target_os = "linux")]
pub fn online_cpu_count() -> usize {
let n = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
if n <= 0 {
return 1;
}
n as usize
}
#[cfg(not(target_os = "linux"))]
pub fn online_cpu_count() -> usize {
1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_online_cpu_count_positive() {
let n = online_cpu_count();
assert!(n >= 1, "online CPU count must be >= 1, got {}", n);
}
#[cfg(target_os = "linux")]
#[test]
fn test_set_affinity_cpu_zero() {
let result = set_thread_affinity(0);
assert!(result.is_ok(), "set_thread_affinity(0) should succeed: {:?}", result);
}
#[cfg(target_os = "linux")]
#[test]
fn test_set_affinity_range_all_cpus() {
let n = online_cpu_count();
let cpus: Vec<usize> = (0..n).collect();
let result = set_thread_affinity_range(&cpus);
assert!(result.is_ok(), "set_thread_affinity_range(all) should succeed: {:?}", result);
}
#[cfg(target_os = "linux")]
#[test]
fn test_set_affinity_out_of_range() {
let result = set_thread_affinity(65535);
let _ = result;
}
}