subms_mpsc_queue/features/
affinity.rs1use std::fmt;
28
29#[derive(Debug)]
31pub enum AffinityError {
32 Unsupported,
34 InvalidCore(usize),
36 OsError(i32),
38}
39
40impl fmt::Display for AffinityError {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 AffinityError::Unsupported => {
44 write!(f, "affinity pinning not supported on this platform")
45 }
46 AffinityError::InvalidCore(c) => write!(f, "invalid core index: {c}"),
47 AffinityError::OsError(e) => write!(f, "os error: {e}"),
48 }
49 }
50}
51
52impl std::error::Error for AffinityError {}
53
54pub fn set_affinity(cores: &[usize]) -> Result<(), AffinityError> {
61 if cores.is_empty() {
62 return Err(AffinityError::InvalidCore(0));
63 }
64 #[cfg(target_os = "linux")]
65 {
66 linux::set_affinity_linux(cores)
67 }
68 #[cfg(target_os = "windows")]
69 {
70 windows::set_affinity_windows(cores)
71 }
72 #[cfg(not(any(target_os = "linux", target_os = "windows")))]
73 {
74 let _ = cores;
76 Err(AffinityError::Unsupported)
77 }
78}
79
80#[cfg(target_os = "linux")]
81mod linux {
82 use super::AffinityError;
83 use std::mem;
84
85 #[repr(C)]
90 struct CpuSetT {
91 bits: [u64; 16], }
93
94 unsafe extern "C" {
95 fn sched_setaffinity(pid: u32, cpusetsize: usize, mask: *const CpuSetT) -> i32;
96 }
97
98 pub(super) fn set_affinity_linux(cores: &[usize]) -> Result<(), AffinityError> {
99 let mut set = CpuSetT { bits: [0u64; 16] };
100 for &c in cores {
101 if c >= 1024 {
102 return Err(AffinityError::InvalidCore(c));
103 }
104 set.bits[c / 64] |= 1u64 << (c % 64);
105 }
106 let rc = unsafe { sched_setaffinity(0, mem::size_of::<CpuSetT>(), &set as *const _) };
107 if rc == 0 {
108 Ok(())
109 } else {
110 Err(AffinityError::OsError(rc))
111 }
112 }
113}
114
115#[cfg(target_os = "windows")]
116mod windows {
117 use super::AffinityError;
118
119 type Handle = *mut core::ffi::c_void;
120 type DWordPtr = usize;
121
122 unsafe extern "system" {
123 fn GetCurrentThread() -> Handle;
124 fn SetThreadAffinityMask(thread: Handle, mask: DWordPtr) -> DWordPtr;
125 }
126
127 pub(super) fn set_affinity_windows(cores: &[usize]) -> Result<(), AffinityError> {
128 let mut mask: usize = 0;
129 let max_bits = usize::BITS as usize;
130 for &c in cores {
131 if c >= max_bits {
132 return Err(AffinityError::InvalidCore(c));
133 }
134 mask |= 1usize << c;
135 }
136 let prev = unsafe { SetThreadAffinityMask(GetCurrentThread(), mask) };
138 if prev == 0 {
139 Err(AffinityError::OsError(0))
140 } else {
141 Ok(())
142 }
143 }
144}
145
146#[cfg(test)]
147#[path = "affinity_tests.rs"]
148mod tests;