#[derive(Clone, Debug)]
pub struct ExecutionContext {
pub threads: usize,
}
impl ExecutionContext {
pub const fn serial() -> Self {
Self { threads: 1 }
}
pub fn with_threads(threads: usize) -> Self {
Self {
threads: threads.max(1),
}
}
pub fn auto() -> Self {
let threads = std::thread::available_parallelism()
.map(std::num::NonZeroUsize::get)
.unwrap_or(1);
Self { threads }
}
pub fn effective_workers(&self, work_units: usize) -> usize {
self.threads.min(work_units).max(1)
}
}
impl Default for ExecutionContext {
fn default() -> Self {
Self::serial()
}
}
#[cfg(test)]
mod tests {
use super::ExecutionContext;
#[test]
fn serial_is_one_thread_and_default() {
assert_eq!(ExecutionContext::serial().threads, 1);
assert_eq!(ExecutionContext::default().threads, 1);
}
#[test]
fn with_threads_clamps_up_to_one() {
assert_eq!(ExecutionContext::with_threads(0).threads, 1);
assert_eq!(ExecutionContext::with_threads(1).threads, 1);
assert_eq!(ExecutionContext::with_threads(8).threads, 8);
}
#[test]
fn auto_is_at_least_one() {
assert!(ExecutionContext::auto().threads >= 1);
}
#[test]
fn effective_workers_clamps_both_sides() {
let ctx = ExecutionContext::with_threads(4);
assert_eq!(ctx.effective_workers(0), 1);
assert_eq!(ctx.effective_workers(1), 1);
assert_eq!(ctx.effective_workers(3), 3);
assert_eq!(ctx.effective_workers(4), 4);
assert_eq!(ctx.effective_workers(64), 4);
let serial = ExecutionContext::serial();
assert_eq!(serial.effective_workers(64), 1);
assert_eq!(serial.effective_workers(0), 1);
}
}