use crate::client::{Client, ConnectionManager};
use std::{
collections::VecDeque,
future::Future,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
time::Duration,
};
use tokio::time::sleep;
pub(crate) enum ProcessResult<W> {
Success { new_work: Vec<W> },
Retry {
work: W,
sleep_duration: Duration,
recreate_client: bool,
},
}
pub(crate) async fn run_workers<W, F, Fut>(
initial_work: VecDeque<W>,
conn_manager: &ConnectionManager,
parallel: usize,
processor: F,
) where
F: Fn(usize, W, Client) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = ProcessResult<W>> + Send,
W: Send + 'static,
{
let work_queue = Arc::new(Mutex::new(initial_work));
let active_workers = Arc::new(AtomicUsize::new(0));
let conn_manager = conn_manager.clone();
let mut handles = vec![];
for worker_index in 0..parallel {
let work_queue = work_queue.clone();
let active_workers = active_workers.clone();
let conn_manager = conn_manager.clone();
let processor = processor.clone();
handles.push(tokio::spawn(async move {
let mut is_active = false;
loop {
let work = {
let mut queue = work_queue.lock().unwrap();
match queue.pop_front() {
Some(w) => {
if !is_active {
active_workers.fetch_add(1, Ordering::SeqCst);
is_active = true;
}
Some(w)
},
None => {
if is_active {
active_workers.fetch_sub(1, Ordering::SeqCst);
is_active = false;
}
None
},
}
};
let Some(work) = work else {
sleep(Duration::from_millis(100)).await;
let queue_len = work_queue.lock().unwrap().len();
let active = active_workers.load(Ordering::SeqCst);
if queue_len == 0 && active == 0 {
break;
}
continue;
};
let client = conn_manager.get(worker_index).await;
match processor(worker_index, work, client.clone()).await {
ProcessResult::Success { new_work } => {
if !new_work.is_empty() {
work_queue.lock().unwrap().extend(new_work);
}
},
ProcessResult::Retry { work, sleep_duration, recreate_client } => {
work_queue.lock().unwrap().push_back(work);
sleep(sleep_duration).await;
if recreate_client {
conn_manager.recreate_client(worker_index, client).await;
}
},
}
}
}));
}
futures::future::join_all(handles).await;
}