use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(target_os = "linux")]
pub(crate) use sys::count;
#[cfg(not(target_os = "linux"))]
#[allow(unused_imports)]
pub(crate) use sys::count;
pub(crate) use sys::{nth, read_here, set_here, set_here_to, CpuMask};
pub(crate) fn available() -> usize {
#[cfg(target_os = "linux")]
if let Some(set) = read_here() {
let n = count(&set);
if n > 0 {
return n;
}
}
std::thread::available_parallelism().map_or(1, |n| n.get())
}
pub(crate) fn oversubscribed(participants: usize) -> bool {
participants > available()
}
pub(crate) fn pin_to(set: &CpuMask, k: usize) {
if let Some(cpu) = nth(set, k) {
set_here_to(cpu);
}
}
#[cfg_attr(not(feature = "parallel"), allow(dead_code))]
pub(crate) struct SavedMask {
mask: core::cell::UnsafeCell<core::mem::MaybeUninit<CpuMask>>,
pinned: AtomicBool,
}
unsafe impl Sync for SavedMask {}
#[cfg_attr(not(feature = "parallel"), allow(dead_code))]
impl SavedMask {
pub(crate) const fn new() -> Self {
SavedMask {
mask: core::cell::UnsafeCell::new(core::mem::MaybeUninit::uninit()),
pinned: AtomicBool::new(false),
}
}
pub(crate) fn save_if_unpinned(&self) {
if self.pinned.load(Ordering::Relaxed) {
return;
}
if let Some(mask) = read_here() {
unsafe { (*self.mask.get()).write(mask) };
self.pinned.store(true, Ordering::Relaxed);
}
}
pub(crate) fn restore(&self) {
if !self.pinned.load(Ordering::Relaxed) {
return;
}
let mask = unsafe { (*self.mask.get()).assume_init_ref() };
if set_here(mask) {
self.pinned.store(false, Ordering::Relaxed);
}
}
}
#[cfg(target_os = "linux")]
mod sys {
use core::mem;
const SETSIZE: usize = 8 * mem::size_of::<libc::cpu_set_t>();
#[derive(Clone, Copy)]
pub(crate) struct CpuMask {
set: libc::cpu_set_t,
}
pub(crate) fn read_here() -> Option<CpuMask> {
let mut set = unsafe { mem::zeroed::<libc::cpu_set_t>() };
let r = unsafe { libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) };
(r == 0).then_some(CpuMask { set })
}
pub(crate) fn set_here(mask: &CpuMask) -> bool {
let r = unsafe { libc::sched_setaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mask.set) };
r == 0
}
pub(crate) fn set_here_to(cpu: usize) -> bool {
if cpu >= SETSIZE {
return false;
}
unsafe {
let mut set = mem::zeroed::<libc::cpu_set_t>();
libc::CPU_ZERO(&mut set);
libc::CPU_SET(cpu, &mut set);
set_here(&CpuMask { set })
}
}
pub(crate) fn count(mask: &CpuMask) -> usize {
let n = unsafe { libc::CPU_COUNT(&mask.set) };
n.max(0) as usize
}
pub(crate) fn nth(mask: &CpuMask, k: usize) -> Option<usize> {
let n = count(mask);
if n == 0 {
return None;
}
let mut want = k % n;
for cpu in 0..SETSIZE {
if unsafe { libc::CPU_ISSET(cpu, &mask.set) } {
if want == 0 {
return Some(cpu);
}
want -= 1;
}
}
None
}
}
#[cfg(not(target_os = "linux"))]
#[allow(dead_code)] mod sys {
#[derive(Clone, Copy)]
pub(crate) struct CpuMask(());
pub(crate) fn read_here() -> Option<CpuMask> {
None
}
pub(crate) fn set_here(_mask: &CpuMask) -> bool {
false
}
pub(crate) fn set_here_to(_cpu: usize) -> bool {
false
}
pub(crate) fn count(_mask: &CpuMask) -> usize {
0
}
pub(crate) fn nth(_mask: &CpuMask, _k: usize) -> Option<usize> {
None
}
}