use std::{
num::NonZeroUsize,
sync::{
Arc,
mpmc::{IntoIter, Receiver, Sender},
mpsc::TryRecvError,
},
thread::{self, Scope, ScopedJoinHandle},
};
use crate::{Gate, GenericThreadpool, channel, num_cpus};
#[allow(unused_imports)]
use crate::ordered::{FilterMapAsync, OrderedThreadpool};
#[allow(unused_imports)]
use std::thread::available_parallelism;
pub struct Threadpool<'scope, 'env, I, O> {
scope: &'scope Scope<'scope, 'env>,
#[allow(unused)]
workers: Vec<ScopedJoinHandle<'scope, ()>>,
in_flight: Gate<usize>,
producers_active: Gate<usize>,
work_submission: Sender<I>,
work_reception: Receiver<O>,
}
impl<'scope, 'env, I, O> Threadpool<'scope, 'env, I, O> {
pub fn new<F>(f: F, scope: &'scope Scope<'scope, 'env>) -> Self
where
F: Fn(I) -> Option<O> + Send + Sync + 'scope,
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
ThreadpoolBuilder::new().build(move |x, _| f(x), scope)
}
fn new_inner<F, N, M>(
f: F,
initializer: N,
scope: &'scope Scope<'scope, 'env>,
num_workers: NonZeroUsize,
blocking_submission: bool,
blocking_workers: bool,
) -> Self
where
F: Fn(I, (&mut M, usize)) -> Option<O> + Send + Sync + 'scope,
N: Fn(usize) -> M + Send + Sync + 'scope,
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
let (work_submission, inbox) = channel(blocking_submission);
let (outbox, work_reception) = channel(blocking_workers);
let in_flight = Gate::new(0usize);
let producers_active = Gate::new(0);
let f = Arc::new(f);
let initializer = Arc::new(initializer);
let workers = (0..num_workers.into())
.map(|id| {
let inbox = inbox.clone();
let outbox = outbox.clone();
let in_flight = in_flight.clone();
let f = f.clone();
let initializer = initializer.clone();
scope.spawn(move || {
let mut thread_state = initializer(id);
for item in inbox {
let result = f(item, (&mut thread_state, id));
if let Some(result) = result {
outbox.send(result).unwrap();
}
in_flight.update(|x| *x = x.saturating_sub(1));
}
})
})
.collect();
Self {
workers,
work_submission,
work_reception,
in_flight,
producers_active,
scope,
}
}
}
impl<'scope, 'env, I, O> GenericThreadpool<'scope, I, O> for Threadpool<'scope, 'env, I, O>
where
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
type Iter<'a>
= ThreadpoolIter<'a, 'scope, 'env, I, O>
where
Self: 'a;
type JoinHandle = ScopedJoinHandle<'scope, ()>;
fn submit(&self, input: I) {
self.in_flight.update(|x| *x += 1);
self.work_submission.send(input).unwrap()
}
fn recv(&self) -> O {
self.work_reception.recv().unwrap()
}
fn try_recv(&self) -> Option<O> {
match self.work_reception.try_recv() {
Ok(val) => Some(val),
Err(TryRecvError::Empty) => None,
Err(err) => panic!("{err}"),
}
}
fn iter(&self) -> Self::Iter<'_> {
ThreadpoolIter(self)
}
fn wait_until_finished(&self) {
self.producers_active.wait_while(|x| *x > 0);
self.in_flight.wait_while(|x| *x > 0);
}
fn producer<T>(&self, iter: T) -> ScopedJoinHandle<'scope, ()>
where
T: IntoIterator<Item = I> + Send + Sync + 'scope,
{
self.producers_active.update(|x| *x += 1);
let in_flight = self.in_flight.clone();
let work_submission = self.work_submission.clone();
let producers_active = self.producers_active.clone();
self.scope.spawn(move || {
for item in iter {
in_flight.update(|x| *x += 1);
work_submission.send(item).unwrap();
}
producers_active.update(|x| *x = x.saturating_sub(1));
})
}
fn consumer<F>(&self, f: F) -> ScopedJoinHandle<'scope, ()>
where
O: Send + Sync + 'scope,
F: Fn(O) + Sync + Send + 'scope,
{
let work_reception = self.work_reception.clone();
self.scope.spawn(move || {
for item in work_reception {
f(item);
}
})
}
}
#[derive(Clone, Debug)]
pub struct ThreadpoolBuilder {
num_workers: NonZeroUsize,
blocking_submission: bool,
blocking_workers: bool,
}
impl ThreadpoolBuilder {
pub fn new() -> ThreadpoolBuilder {
ThreadpoolBuilder {
num_workers: num_cpus(),
blocking_submission: false,
blocking_workers: false,
}
}
pub fn initializer<I, M>(self, initializer: I) -> InitializedThreadpoolBuilder<I>
where
I: Fn(usize) -> M,
{
let ThreadpoolBuilder {
num_workers,
blocking_submission,
blocking_workers,
} = self;
InitializedThreadpoolBuilder {
initializer,
num_workers,
blocking_submission,
blocking_workers,
}
}
pub fn num_workers(self, num_workers: NonZeroUsize) -> Self {
Self {
num_workers,
..self
}
}
pub fn blocking_submission(self) -> Self {
Self {
blocking_submission: true,
..self
}
}
pub fn blocking_workers(self) -> Self {
Self {
blocking_workers: true,
..self
}
}
pub fn build<'scope, 'env, F, I, O>(
self,
f: F,
scope: &'scope Scope<'scope, 'env>,
) -> Threadpool<'scope, 'env, I, O>
where
F: Fn(I, usize) -> Option<O> + Send + Sync + 'scope,
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
Threadpool::new_inner(
move |i, (_, id)| f(i, id),
|_| (),
scope,
self.num_workers,
self.blocking_submission,
self.blocking_workers,
)
}
}
pub struct InitializedThreadpoolBuilder<N> {
initializer: N,
num_workers: NonZeroUsize,
blocking_submission: bool,
blocking_workers: bool,
}
impl<N> InitializedThreadpoolBuilder<N> {
pub fn initializer<I, M>(self, initializer: I) -> InitializedThreadpoolBuilder<I>
where
I: Fn(usize) -> M,
{
InitializedThreadpoolBuilder {
initializer,
..self
}
}
pub fn num_workers(self, num_workers: NonZeroUsize) -> Self {
Self {
num_workers,
..self
}
}
pub fn blocking_submission(self) -> Self {
Self {
blocking_submission: true,
..self
}
}
pub fn blocking_workers(self) -> Self {
Self {
blocking_workers: true,
..self
}
}
pub fn build<'scope, 'env, F, I, O, M>(
self,
f: F,
scope: &'scope Scope<'scope, 'env>,
) -> Threadpool<'scope, 'env, I, O>
where
F: Fn(I, (&mut M, usize)) -> Option<O> + Send + Sync + 'scope,
N: Fn(usize) -> M + Send + Sync + 'scope,
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
Threadpool::new_inner(
f,
self.initializer,
scope,
self.num_workers,
self.blocking_submission,
self.blocking_workers,
)
}
}
pub struct ThreadpoolIter<'a, 'scope, 'env, I, O>(&'a Threadpool<'scope, 'env, I, O>);
impl<'a, 'scope, 'env, I, O> Iterator for ThreadpoolIter<'a, 'scope, 'env, I, O> {
type Item = O;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.work_reception.try_recv() {
Ok(item) => return Some(item),
Err(TryRecvError::Empty) => {
if self.0.in_flight.check() == 0 && self.0.producers_active.check() == 0 {
return None;
}
}
Err(err) => panic!("{err}"),
}
thread::yield_now();
}
}
}
impl<'scope, 'env, I, O> Extend<I> for Threadpool<'scope, 'env, I, O>
where
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
fn extend<T: IntoIterator<Item = I>>(&mut self, iter: T) {
self.submit_all(iter);
}
}
impl<'scope, 'env, I, O> IntoIterator for Threadpool<'scope, 'env, I, O> {
type Item = O;
type IntoIter = IntoIter<O>;
fn into_iter(self) -> Self::IntoIter {
drop(self.work_submission);
self.work_reception.into_iter()
}
}
pub trait FilterMapAsyncUnordered<'scope> {
fn filter_map_async_unordered<'env, F, O>(
self,
f: F,
scope: &'scope Scope<'scope, 'env>,
) -> impl Iterator<Item = O> + 'scope
where
O: Send + Sync + 'scope,
Self: Iterator,
Self::Item: Send + Sync + 'scope,
F: Fn(Self::Item) -> Option<O> + Send + Sync + 'scope;
}
impl<'scope, T> FilterMapAsyncUnordered<'scope> for T
where
T: Iterator + Send + Sync + 'scope,
{
fn filter_map_async_unordered<'env, F, O>(
self,
f: F,
scope: &'scope Scope<'scope, 'env>,
) -> impl Iterator<Item = O> + 'scope
where
O: Send + Sync + 'scope,
Self: Iterator + 'scope,
T::Item: Send + Sync + 'scope,
F: Fn(T::Item) -> Option<O> + Send + Sync + 'scope,
{
let pool = Threadpool::new(f, scope);
pool.producer(self);
pool.into_iter()
}
}