use crate::{
bamio::BamDataSource,
engine::BUFWRITER_CAP,
jobqueue::IntervalJob,
output::{write_multiple_outputs, OrderedPileupOutput},
params::PileupParams,
pileup_iterator::PileupIterator,
refseq::RefSeqHandle,
utils::OutputWriter,
};
use std::sync::{Arc, Condvar, Mutex};
pub struct ThreadSignal {
lock: Mutex<usize>, cvar: Condvar, }
impl ThreadSignal {
pub fn wait_while(&self) {
std::mem::drop(
self.cvar
.wait_while(self.lock.lock().unwrap(), |free| *free == 0)
.unwrap(),
)
}
pub fn mark_running(&self) {
*self.lock.lock().unwrap() -= 1;
self.cvar.notify_one();
}
pub fn mark_done(&self) {
*self.lock.lock().unwrap() += 1;
self.cvar.notify_one();
}
}
pub struct PileupWorker {
jobid: usize,
handle: Option<std::thread::JoinHandle<()>>,
notify: Arc<ThreadSignal>,
}
impl PileupWorker {
pub fn new(notify: Arc<ThreadSignal>) -> Self {
Self {
jobid: 0,
handle: None,
notify,
}
}
fn is_finished(&mut self) -> bool {
if let Some(ref handle) = self.handle {
if handle.is_finished() {
self.handle.take().unwrap().join().unwrap();
return true;
} else {
return false;
}
}
true
}
pub fn run<T>(
&mut self,
id: usize,
params: PileupParams,
job: IntervalJob,
src: Vec<BamDataSource>,
refseq: RefSeqHandle,
) where
T: OrderedPileupOutput + 'static,
{
self.jobid = id;
let notify = Arc::clone(&self.notify);
self.handle = Some(std::thread::spawn(move || {
notify.mark_running();
let mut out = OutputWriter::new(&job.out, BUFWRITER_CAP, true, false).unwrap();
let mut iterator = PileupIterator::<T>::from_query(&src, refseq, &job.interval, ¶ms)
.expect("Failed to initalize thread pileup iterator");
while iterator.advance().expect("pileup failed").is_some() {
write_multiple_outputs(&iterator.ctx(), iterator.current(), out.get()).expect("error writing");
}
out.flush().expect("Failed to flush thread writer");
*job.done.lock().unwrap() = true;
notify.mark_done();
}));
}
}
pub struct ThreadPool {
workers: Vec<PileupWorker>,
notify: Arc<ThreadSignal>,
}
impl ThreadPool {
pub fn new(n_threads: usize) -> Self {
let notify = Arc::new(ThreadSignal {
lock: Mutex::new(n_threads),
cvar: Condvar::new(),
});
let mut s = Self {
notify,
workers: Vec::with_capacity(n_threads),
};
(0..n_threads).for_each(|_| s.workers.push(PileupWorker::new(Arc::clone(&s.notify))));
s
}
pub fn get_available(&mut self) -> Option<&mut PileupWorker> {
self.notify.wait_while();
self.workers.iter_mut().find_map(|w| w.is_finished().then_some(w))
}
}