use std::sync::atomic::{AtomicU32, Ordering};
static NEXT_FOREIGN_ID: AtomicU32 = AtomicU32::new(u32::MAX);
thread_local! {
static THIS_WORKER: std::cell::OnceCell<u32> = const { std::cell::OnceCell::new() };
}
pub fn worker_id() -> u32 {
THIS_WORKER.with(|cell| {
*cell.get_or_init(|| {
if let Some(idx) = rayon::current_thread_index() {
idx as u32
} else {
NEXT_FOREIGN_ID.fetch_sub(1, Ordering::Relaxed)
}
})
})
}
#[inline]
pub fn max_workers() -> u32 {
rayon::current_num_threads() as u32
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use std::sync::Mutex;
#[test]
fn worker_id_stable_within_thread() {
let a = worker_id();
let b = worker_id();
assert_eq!(a, b);
}
#[test]
fn worker_ids_are_unique_in_rayon_pool() {
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(4)
.build()
.unwrap();
let observed: Mutex<HashSet<u32>> = Mutex::new(HashSet::new());
pool.install(|| {
(0..1024usize).for_each(|_| {
observed.lock().unwrap().insert(worker_id());
});
});
let ids = observed.into_inner().unwrap();
assert!(ids.len() <= 4);
for id in ids {
assert!(id < 4, "rayon worker id {id} out of pool range");
}
}
#[test]
fn foreign_thread_id_is_distinct_from_rayon_range() {
let id = worker_id();
assert!(
id >= u32::MAX - 1024 || id < max_workers(),
"expected foreign id near u32::MAX or rayon pool id; got {id}"
);
}
}