1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std;
use std::fmt;
use std::thread::JoinHandle;
pub struct Joiner {
joiner: Option<JoinHandle<()>>,
}
impl fmt::Debug for Joiner {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.joiner.is_some() {
write!(f, "Joiner {{ joiner: Some(...) }}")
} else {
write!(f, "Joiner {{ joiner: None }}")
}
}
}
impl Joiner {
pub fn new(joiner: JoinHandle<()>) -> Joiner {
Joiner { joiner: Some(joiner) }
}
pub fn detach(mut self) {
let _ = unwrap!(self.joiner.take());
}
}
impl Drop for Joiner {
fn drop(&mut self) {
if let Some(joiner) = self.joiner.take() {
unwrap!(joiner.join());
}
}
}
pub fn named<S, F>(thread_name: S, func: F) -> Joiner
where S: Into<String>,
F: FnOnce() + Send + 'static
{
let thread_name: String = thread_name.into();
let join_handle_res = std::thread::Builder::new().name(thread_name).spawn(func);
Joiner::new(unwrap!(join_handle_res))
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::{Duration, Instant};
#[test]
fn raii_thread_joiner() {
const SLEEP_DURATION_DAEMON: u64 = 150;
const SLEEP_DURATION_MANAGED: u64 = SLEEP_DURATION_DAEMON * 3;
{
let time_before = Instant::now();
{
named("JoinerTestDaemon",
move || { thread::sleep(Duration::from_millis(SLEEP_DURATION_DAEMON)); })
.detach();
}
let diff = time_before.elapsed();
assert!(diff < Duration::from_millis(SLEEP_DURATION_DAEMON));
}
{
let time_before = Instant::now();
{
let _joiner = named("JoinerTestManaged", move || {
thread::sleep(Duration::from_millis(SLEEP_DURATION_MANAGED));
});
}
let diff = time_before.elapsed();
assert!(diff >= Duration::from_millis(SLEEP_DURATION_MANAGED));
}
}
}