use std::fmt;
use std::num::NonZeroUsize;
use std::sync::Arc;
use crate::compare::{Algorithm, Comparator};
use crate::{Error, Result};
#[derive(Clone)]
pub struct Oinkie {
pool: Arc<rayon::ThreadPool>,
}
impl Oinkie {
pub fn new() -> Result<Oinkie> {
Oinkie::builder().build()
}
pub fn builder() -> OinkieBuilder {
OinkieBuilder { threads: None }
}
pub fn threads(&self) -> NonZeroUsize {
NonZeroUsize::new(self.pool.current_num_threads())
.expect("a pool is built with at least one thread")
}
pub fn comparator(&self, algorithm: &Algorithm) -> Comparator {
Comparator::new(algorithm, Arc::clone(&self.pool))
}
pub fn install<OP, R>(&self, op: OP) -> R
where
OP: FnOnce() -> R + Send,
R: Send,
{
self.pool.install(op)
}
}
impl fmt::Debug for Oinkie {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Oinkie")
.field("threads", &self.threads())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct OinkieBuilder {
threads: Option<NonZeroUsize>,
}
impl OinkieBuilder {
pub fn threads(mut self, n: NonZeroUsize) -> OinkieBuilder {
self.threads = Some(n);
self
}
pub fn build(self) -> Result<Oinkie> {
let threads = self
.threads
.or_else(|| std::thread::available_parallelism().ok())
.unwrap_or(NonZeroUsize::MIN);
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(threads.get())
.thread_name(|i| format!("oinkie-{i}"))
.build()
.map_err(|e| Error::ThreadPool(threads, e.to_string()))?;
Ok(Oinkie {
pool: Arc::new(pool),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_computes_on_as_many_threads_as_it_was_given() {
for n in [1, 3] {
let oinkie = Oinkie::builder()
.threads(NonZeroUsize::new(n).unwrap())
.build()
.unwrap();
assert_eq!(oinkie.threads().get(), n);
assert_eq!(oinkie.install(rayon::current_num_threads), n);
}
}
#[test]
fn by_default_it_takes_one_thread_per_core() {
let cores = std::thread::available_parallelism().unwrap();
assert_eq!(Oinkie::new().unwrap().threads(), cores);
}
#[test]
fn two_keep_their_own_threads() {
let one = Oinkie::builder()
.threads(NonZeroUsize::new(1).unwrap())
.build()
.unwrap();
let two = Oinkie::builder()
.threads(NonZeroUsize::new(2).unwrap())
.build()
.unwrap();
assert_eq!(one.install(rayon::current_num_threads), 1);
assert_eq!(two.install(rayon::current_num_threads), 2);
let shared = two.clone();
assert!(Arc::ptr_eq(&shared.pool, &two.pool));
}
}