#![doc = include_str!("../README.md")]
#![warn(missing_docs, missing_debug_implementations, unreachable_pub)]
#![forbid(unsafe_code)]
use std::any::Any;
use std::fmt;
use std::mem;
use std::thread::{JoinHandle, Result as ThreadResult};
pub struct ThreadGuard<T>(
#[allow(clippy::type_complexity)]
Option<(
JoinHandle<T>,
Box<dyn FnOnce(bool, JoinHandle<T>) -> ThreadResult<T> + Send>,
)>,
);
impl<T> ThreadGuard<T> {
pub fn new(handle: JoinHandle<T>) -> Self {
let action =
Box::new(move |_run_post_action, join_handle: JoinHandle<T>| join_handle.join());
Self(Some((handle, action)))
}
pub fn with_pre_action<U, F>(handle: JoinHandle<T>, pre_action: F) -> Self
where
for<'a> F: FnOnce(&JoinHandle<T>) -> U + Send + 'a,
{
Self::with_actions(handle, pre_action, |_, _| {})
}
pub fn with_post_action<F>(handle: JoinHandle<T>, post_action: F) -> Self
where
for<'a> F: FnOnce(ThreadResult<T>) + Send + 'a,
{
Self::with_actions(handle, |_| {}, |_, result| post_action(result))
}
pub fn with_actions<U, F, G>(handle: JoinHandle<T>, pre_action: F, post_action: G) -> Self
where
for<'a> F: FnOnce(&JoinHandle<T>) -> U + Send + 'a,
for<'a> G: FnOnce(U, ThreadResult<T>) + Send + 'a,
{
let action = Box::new(move |run_post_action, join_handle| {
let arg = pre_action(&join_handle);
let result = join_handle.join();
if run_post_action {
post_action(arg, result);
return Err(Box::new(()) as Box<dyn Any + Send>);
}
result
});
Self(Some((handle, action)))
}
pub fn join(mut self) -> ThreadResult<T> {
let (handle, action) = self.0.take().unwrap();
mem::forget(self);
action(false, handle)
}
}
impl<T> Drop for ThreadGuard<T> {
fn drop(&mut self) {
let (handle, action) = self.0.take().unwrap();
let _ = action(true, handle);
}
}
impl<T> fmt::Debug for ThreadGuard<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ThreadGuard").finish_non_exhaustive()
}
}