src/tools/parallel_handler.rs: join all threads in drop handler

This commit is contained in:
Dietmar Maurer 2020-09-26 08:26:14 +02:00
parent 69b92fab7e
commit 1c13afa8f9

View File

@ -9,20 +9,6 @@ pub struct SendHandle<I> {
abort: Arc<Mutex<Option<String>>>, abort: Arc<Mutex<Option<String>>>,
} }
/// A thread pool which run the supplied closure
///
/// The send command sends data to the worker threads. If one handler
/// returns an error, we mark the channel as failed and it is no
/// longer possible to send data.
///
/// When done, the 'complete()' method needs to be called to check for
/// outstanding errors.
pub struct ParallelHandler<I> {
handles: Vec<JoinHandle<()>>,
name: String,
input: SendHandle<I>,
}
impl <I: Send + Sync +'static> SendHandle<I> { impl <I: Send + Sync +'static> SendHandle<I> {
/// Returns the first error happened, if any /// Returns the first error happened, if any
@ -42,6 +28,20 @@ impl <I: Send + Sync +'static> SendHandle<I> {
} }
} }
/// A thread pool which run the supplied closure
///
/// The send command sends data to the worker threads. If one handler
/// returns an error, we mark the channel as failed and it is no
/// longer possible to send data.
///
/// When done, the 'complete()' method needs to be called to check for
/// outstanding errors.
pub struct ParallelHandler<I> {
handles: Vec<JoinHandle<()>>,
name: String,
input: Option<SendHandle<I>>,
}
impl <I> Clone for SendHandle<I> { impl <I> Clone for SendHandle<I> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { input: self.input.clone(), abort: self.abort.clone() } Self { input: self.input.clone(), abort: self.abort.clone() }
@ -94,40 +94,74 @@ impl <I: Send + Sync + 'static> ParallelHandler<I> {
Self { Self {
handles, handles,
name: name.to_string(), name: name.to_string(),
input: SendHandle { input: Some(SendHandle {
input: input_tx, input: input_tx,
abort, abort,
}, }),
} }
} }
/// Returns a cloneable channel to send data to the worker threads /// Returns a cloneable channel to send data to the worker threads
pub fn channel(&self) -> SendHandle<I> { pub fn channel(&self) -> SendHandle<I> {
self.input.clone() self.input.as_ref().unwrap().clone()
} }
/// Send data to the worker threads /// Send data to the worker threads
pub fn send(&self, input: I) -> Result<(), Error> { pub fn send(&self, input: I) -> Result<(), Error> {
self.input.send(input)?; self.input.as_ref().unwrap().send(input)?;
Ok(()) Ok(())
} }
/// Wait for worker threads to complete and check for errors /// Wait for worker threads to complete and check for errors
pub fn complete(self) -> Result<(), Error> { pub fn complete(mut self) -> Result<(), Error> {
self.input.check_abort()?; self.input.as_ref().unwrap().check_abort()?;
drop(self.input); drop(self.input.take());
let mut msg = Vec::new();
for (i, handle) in self.handles.into_iter().enumerate() { let msg_list = self.join_threads();
if let Err(panic) = handle.join() {
match panic.downcast::<&str>() { if msg_list.is_empty() {
Ok(panic_msg) => msg.push(format!("thread {} ({}) paniced: {}", self.name, i, panic_msg)),
Err(_) => msg.push(format!("thread {} ({}) paniced", self.name, i)),
}
}
}
if msg.is_empty() {
return Ok(()); return Ok(());
} }
Err(format_err!("{}", msg.join("\n"))) Err(format_err!("{}", msg_list.join("\n")))
}
fn join_threads(&mut self) -> Vec<String> {
let mut msg_list = Vec::new();
let mut i = 0;
loop {
let handle = match self.handles.pop() {
Some(handle) => handle,
None => break,
};
if let Err(panic) = handle.join() {
match panic.downcast::<&str>() {
Ok(panic_msg) => msg_list.push(
format!("thread {} ({}) paniced: {}", self.name, i, panic_msg)
),
Err(_) => msg_list.push(
format!("thread {} ({}) paniced", self.name, i)
),
}
}
i += 1;
}
msg_list
}
}
// Note: We make sure that all threads will be joined
impl <I> Drop for ParallelHandler<I> {
fn drop(&mut self) {
drop(self.input.take());
loop {
match self.handles.pop() {
Some(handle) => {
let _ = handle.join();
}
None => break,
}
}
} }
} }