1#![doc = include_str!("../README.md")]
2#![warn(missing_docs, missing_debug_implementations, unreachable_pub)]
3#![forbid(unsafe_code)]
4
5use std::any::Any;
6use std::fmt;
7use std::mem;
8use std::thread::{JoinHandle, Result as ThreadResult};
9
10pub struct ThreadGuard<T>(
19 #[allow(clippy::type_complexity)]
20 Option<(
21 JoinHandle<T>,
22 Box<dyn FnOnce(bool, JoinHandle<T>) -> ThreadResult<T> + Send>,
23 )>,
24);
25
26impl<T> ThreadGuard<T> {
27 pub fn new(handle: JoinHandle<T>) -> Self {
29 let action =
30 Box::new(move |_run_post_action, join_handle: JoinHandle<T>| join_handle.join());
31
32 Self(Some((handle, action)))
33 }
34
35 pub fn with_pre_action<U, F>(handle: JoinHandle<T>, pre_action: F) -> Self
37 where
38 for<'a> F: FnOnce(&JoinHandle<T>) -> U + Send + 'a,
39 {
40 Self::with_actions(handle, pre_action, |_, _| {})
41 }
42
43 pub fn with_post_action<F>(handle: JoinHandle<T>, post_action: F) -> Self
45 where
46 for<'a> F: FnOnce(ThreadResult<T>) + Send + 'a,
47 {
48 Self::with_actions(handle, |_| {}, |_, result| post_action(result))
49 }
50
51 pub fn with_actions<U, F, G>(handle: JoinHandle<T>, pre_action: F, post_action: G) -> Self
54 where
55 for<'a> F: FnOnce(&JoinHandle<T>) -> U + Send + 'a,
56 for<'a> G: FnOnce(U, ThreadResult<T>) + Send + 'a,
57 {
58 let action = Box::new(move |run_post_action, join_handle| {
59 let arg = pre_action(&join_handle);
60 let result = join_handle.join();
61 if run_post_action {
62 post_action(arg, result);
63
64 return Err(Box::new(()) as Box<dyn Any + Send>);
68 }
69
70 result
71 });
72
73 Self(Some((handle, action)))
74 }
75
76 pub fn join(mut self) -> ThreadResult<T> {
78 let (handle, action) = self.0.take().unwrap();
79 mem::forget(self);
80
81 action(false, handle)
82 }
83}
84
85impl<T> Drop for ThreadGuard<T> {
86 fn drop(&mut self) {
87 let (handle, action) = self.0.take().unwrap();
88 let _ = action(true, handle);
89 }
90}
91
92impl<T> fmt::Debug for ThreadGuard<T> {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 f.debug_struct("ThreadGuard").finish_non_exhaustive()
95 }
96}