use std::{
collections::VecDeque,
num::NonZeroUsize,
sync::{
Arc, Mutex,
mpmc::{IntoIter, Receiver, Sender},
mpsc::TryRecvError,
},
thread::{self, Scope, ScopedJoinHandle, scope},
};
use crate::{Gate, GenericThreadpool, channel, num_cpus};
#[allow(unused_imports)]
use crate::unordered::{Threadpool, ThreadpoolBuilder};
#[allow(unused_imports)]
use std::thread::available_parallelism;
pub struct OrderedThreadpool<'scope, 'env, I, O> {
scope: &'scope Scope<'scope, 'env>,
#[allow(unused)]
workers: Vec<ScopedJoinHandle<'scope, ()>>,
#[allow(unused)]
filer: ScopedJoinHandle<'scope, ()>,
in_flight: Gate<usize>,
producers_active: Gate<usize>,
work_submission: Sender<(usize, I)>,
work_reception: Receiver<O>,
job_index: Arc<Mutex<usize>>,
}
impl<'scope, 'env, I, O> OrderedThreadpool<'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,
{
OrderedThreadpoolBuilder::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, 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, filer_inbox) = channel(blocking_workers);
let (filer_outbox, work_reception) = channel(blocking_workers);
let in_flight = Gate::new(0usize);
let job_index = Arc::new(Mutex::new(0));
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 f = f.clone();
let initializer = initializer.clone();
scope.spawn(move || {
let mut thread_state = initializer(id);
for (index, item) in inbox {
let result = f(item, (&mut thread_state, id, index));
outbox.send((index, result)).unwrap();
}
})
})
.collect();
let filer = {
let in_flight = in_flight.clone();
scope.spawn(move || {
let mut buffer: VecDeque<(usize, Option<O>)> = VecDeque::new();
let mut least_index = 0;
for (index, result) in filer_inbox {
if index == least_index {
if let Some(item) = result {
filer_outbox.send(item).unwrap()
}
in_flight.update(|x| *x = x.saturating_sub(1));
least_index += 1;
while buffer
.front()
.map(|item| item.0 == least_index)
.unwrap_or(false)
{
if let Some(item) = buffer.pop_front().unwrap().1 {
filer_outbox.send(item).unwrap()
}
in_flight.update(|x| *x = x.saturating_sub(1));
least_index += 1;
}
} else {
let insert_index = buffer
.binary_search_by_key(&index, |&(i, _)| i)
.expect_err("index should never already be present in the buffer");
buffer.insert(insert_index, (index, result));
}
}
for (_, item) in buffer {
if let Some(item) = item {
filer_outbox.send(item).unwrap();
}
in_flight.update(|x| *x = x.saturating_sub(1));
least_index += 1;
}
})
};
Self {
workers,
work_submission,
work_reception,
in_flight,
producers_active,
scope,
job_index,
filer,
}
}
pub fn filter_map<'a, T>(&'a self, iter: T) -> OrderedThreadpoolIter<'a, 'scope, 'env, I, O>
where
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
T: IntoIterator<Item = I>,
{
self.submit_all(iter);
self.iter()
}
pub fn filter_map_async<'a, T>(
&'a self,
iter: T,
) -> OrderedThreadpoolIter<'a, 'scope, 'env, I, O>
where
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
T: IntoIterator<Item = I> + Send + Sync + 'scope,
{
self.producer(iter);
self.iter()
}
}
impl<'scope, 'env, I, O> GenericThreadpool<'scope, I, O> for OrderedThreadpool<'scope, 'env, I, O>
where
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
type Iter<'a>
= OrderedThreadpoolIter<'a, 'scope, 'env, I, O>
where
Self: 'a;
type JoinHandle = ScopedJoinHandle<'scope, ()>;
fn submit(&self, input: I) {
let mut job_index = self.job_index.lock().unwrap();
self.in_flight.update(|x| *x += 1);
self.work_submission.send((*job_index, input)).unwrap();
*job_index += 1;
}
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) -> OrderedThreadpoolIter<'_, 'scope, 'env, I, O> {
OrderedThreadpoolIter(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
I: Send + Sync + 'scope,
T: IntoIterator<Item = I> + Send + Sync + 'scope,
{
self.producers_active.update(|x| *x += 1);
let in_flight = self.in_flight.clone();
let job_index = self.job_index.clone();
let work_submission = self.work_submission.clone();
let producers_active = self.producers_active.clone();
self.scope.spawn(move || {
for item in iter {
let mut job_index = job_index.lock().unwrap();
in_flight.update(|x| *x += 1);
work_submission.send((*job_index, item)).unwrap();
*job_index += 1;
}
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 OrderedThreadpoolBuilder {
num_workers: NonZeroUsize,
blocking_submission: bool,
blocking_workers: bool,
}
impl OrderedThreadpoolBuilder {
pub fn new() -> OrderedThreadpoolBuilder {
OrderedThreadpoolBuilder {
num_workers: num_cpus(),
blocking_submission: false,
blocking_workers: false,
}
}
pub fn initializer<I, M>(self, initializer: I) -> InitializedOrderedThreadpoolBuilder<I>
where
I: Fn(usize) -> M,
{
let OrderedThreadpoolBuilder {
num_workers,
blocking_submission,
blocking_workers,
} = self;
InitializedOrderedThreadpoolBuilder {
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>,
) -> OrderedThreadpool<'scope, 'env, I, O>
where
F: Fn(I, (usize, usize)) -> Option<O> + Send + Sync + 'scope,
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
OrderedThreadpool::new_inner(
move |i, (_, id, jid)| f(i, (id, jid)),
|_| (),
scope,
self.num_workers,
self.blocking_submission,
self.blocking_workers,
)
}
}
pub struct InitializedOrderedThreadpoolBuilder<N> {
initializer: N,
num_workers: NonZeroUsize,
blocking_submission: bool,
blocking_workers: bool,
}
impl<N> InitializedOrderedThreadpoolBuilder<N> {
pub fn initializer<I, M>(self, initializer: I) -> InitializedOrderedThreadpoolBuilder<I>
where
I: Fn(usize) -> M,
{
InitializedOrderedThreadpoolBuilder {
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>,
) -> OrderedThreadpool<'scope, 'env, I, O>
where
F: Fn(I, (&mut M, usize, usize)) -> Option<O> + Send + Sync + 'scope,
N: Fn(usize) -> M + Send + Sync + 'scope,
I: Send + Sync + 'scope,
O: Send + Sync + 'scope,
{
OrderedThreadpool::new_inner(
f,
self.initializer,
scope,
self.num_workers,
self.blocking_submission,
self.blocking_workers,
)
}
}
pub struct OrderedThreadpoolIter<'a, 'scope, 'env, I, O>(&'a OrderedThreadpool<'scope, 'env, I, O>);
impl<'a, 'scope, 'env, I, O> Iterator for OrderedThreadpoolIter<'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 OrderedThreadpool<'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 OrderedThreadpool<'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 FilterMapAsync<'scope> {
fn filter_map_async<'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> FilterMapAsync<'scope> for T
where
T: Iterator + Send + Sync + 'scope,
{
fn filter_map_async<'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 = OrderedThreadpool::new(f, scope);
pool.producer(self);
pool.into_iter()
}
}
pub trait FilterMapMultithread {
fn filter_map_multithread<F, O>(self, f: F) -> Vec<O>
where
O: Send + Sync,
Self: Iterator,
Self::Item: Send + Sync,
F: Fn(Self::Item) -> Option<O> + Send + Sync;
}
impl<T> FilterMapMultithread for T
where
T: Iterator + Send + Sync,
{
fn filter_map_multithread<F, O>(self, f: F) -> Vec<O>
where
O: Send + Sync,
Self: Iterator,
T::Item: Send + Sync,
F: Fn(T::Item) -> Option<O> + Send + Sync,
{
scope(|scope| {
let pool = OrderedThreadpool::new(f, scope);
pool.submit_all(self);
pool.into_iter().collect()
})
}
}