use crate::bamio::OutputDataDest;
use crate::engine::BUFWRITER_CAP;
use crate::errors::Error;
use crate::position_queue::GenomeInterval;
use crate::utils::temp_fname;
use crate::utils::OutputWriter;
use crossbeam::channel::{unbounded, Receiver, Sender};
use log::{info, warn};
use std::collections::VecDeque;
use std::fs::File;
use std::io::BufReader;
use std::sync::{Arc, Mutex};
pub static FILE_MERGE_SINGLETON: Mutex<Vec<OutputDataDest>> = Mutex::new(vec![]);
pub struct IntervalJobInner {
pub out: OutputDataDest,
pub interval: GenomeInterval,
pub done: Mutex<bool>,
}
impl IntervalJobInner {
fn new(interval: &GenomeInterval) -> Self {
Self {
out: OutputDataDest::from_string(&temp_fname(
&format!("{}:{}-{}", interval.name, interval.start, interval.end),
"",
".temp",
)),
done: Mutex::new(false),
interval: interval.clone(),
}
}
}
pub type IntervalJob = Arc<IntervalJobInner>;
pub struct IntervalJobs {
map: VecDeque<(GenomeInterval, Vec<IntervalJob>)>,
pub queue: VecDeque<IntervalJob>,
handle: std::thread::JoinHandle<()>,
s: Sender<Vec<IntervalJob>>,
}
impl IntervalJobs {
pub fn new(
intervals: &[GenomeInterval],
min_coords_per_thread: Option<i64>,
threads: i64,
output: OutputDataDest,
) -> Self {
let mut map: VecDeque<(GenomeInterval, Vec<IntervalJob>)> = VecDeque::new();
let mut queue: VecDeque<IntervalJob> = VecDeque::new();
let mut lock = FILE_MERGE_SINGLETON.lock().unwrap();
for interval in intervals {
let chunks = match min_coords_per_thread {
Some(min_coords_per_thread) => if interval.len() < min_coords_per_thread {
interval.chunks(min_coords_per_thread)
} else {
interval.n_chunks(threads)
}
.map(|c| Arc::new(IntervalJobInner::new(&c)))
.collect::<Vec<IntervalJob>>(),
None => {
vec![Arc::new(IntervalJobInner::new(interval))]
}
};
chunks.iter().for_each(|c| {
queue.push_back(c.clone());
lock.push(c.out.clone());
});
map.push_back((interval.clone(), chunks.clone()));
}
let (s, r): (Sender<Vec<IntervalJob>>, Receiver<Vec<IntervalJob>>) = unbounded();
let handle = std::thread::spawn(move || {
let mut main_writer = OutputWriter::new(&output, BUFWRITER_CAP, true, false).unwrap();
while let Ok(temps) = r.recv() {
for tmp in temps {
match tmp.out {
OutputDataDest::Stdout => {
panic!("cannot merge from stdout! Critical error")
}
OutputDataDest::File(ref f) => {
match File::open(f) {
Err(e) => {
match e.kind() {
std::io::ErrorKind::NotFound => (),
_ => panic!("Failed to open output file for merging: {}", e),
};
}
Ok(f) => {
let mut reader = BufReader::with_capacity(2 * 1024 * 1024, f);
if let Err(e) = std::io::copy(&mut reader, &mut main_writer.get()) {
match e.kind() {
std::io::ErrorKind::Interrupted => (),
_ => panic!("{e}"),
}
}
}
}
if let Err(e) = std::fs::remove_file(f) {
match e.kind() {
std::io::ErrorKind::NotFound => (),
_ => panic!("{}", e),
}
}
}
}
}
}
main_writer.flush().expect("Failed to flush merge writer");
});
Self { map, handle, queue, s }
}
pub fn is_completed(&self) -> bool {
self.map.is_empty()
}
pub fn merge_completed(&mut self) -> Result<(), Error> {
let mut done = 0;
if let Some((interval, pending)) = self.map.front() {
for tmp in pending {
if *tmp.done.lock().unwrap() {
done += 1;
}
}
assert!(done <= pending.len());
if done == pending.len() {
info!("Finished ref {}", interval.name);
let (_, to_merge) = self.map.pop_front().unwrap();
self.s.send(to_merge)?;
}
}
Ok(())
}
pub fn conclude(mut self) -> Result<(), Error> {
self.merge_completed()?;
drop(self.s);
self.handle.join().expect("Failed to join writer thread");
Ok(())
}
}
pub fn setup_exit_handler() {
ctrlc::set_handler(|| {
warn!("Received termination signal. Cleaning up intermediate files...");
if let Ok(outputs) = FILE_MERGE_SINGLETON.lock() {
for t in outputs.iter() {
match t {
OutputDataDest::Stdout => (),
OutputDataDest::File(ref f) => {
if let Err(e) = std::fs::remove_file(f) {
match e.kind() {
std::io::ErrorKind::NotFound => (),
_ => eprintln!("{e}"),
}
}
}
}
}
}
std::process::exit(130);
})
.expect("Failed to set exit handler")
}