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
|
//! **J**oin **O**n **D**rop thread (`jod_thread`) is a thin wrapper around `std::thread`,
//! which makes sure that by default all threads are joined.
use std::fmt;
/// Like `thread::JoinHandle`, but joins the thread on drop and propagates
/// panics by default.
pub struct JoinHandle<T = ()>(Option<std::thread::JoinHandle<T>>);
impl<T> fmt::Debug for JoinHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("JoinHandle { .. }")
}
}
impl<T> Drop for JoinHandle<T> {
fn drop(&mut self) {
if let Some(inner) = self.0.take() {
let res = inner.join();
if res.is_err() && !std::thread::panicking() {
res.unwrap();
}
}
}
}
impl<T> JoinHandle<T> {
pub fn thread(&self) -> &std::thread::Thread {
self.0.as_ref().unwrap().thread()
}
pub fn join(mut self) -> T {
let inner = self.0.take().unwrap();
inner.join().unwrap()
}
pub fn detach(mut self) -> std::thread::JoinHandle<T> {
let inner = self.0.take().unwrap();
inner
}
}
impl<T> From<std::thread::JoinHandle<T>> for JoinHandle<T> {
fn from(inner: std::thread::JoinHandle<T>) -> JoinHandle<T> {
JoinHandle(Some(inner))
}
}
#[derive(Debug)]
pub struct Builder(std::thread::Builder);
impl Builder {
pub fn new() -> Builder {
Builder(std::thread::Builder::new())
}
pub fn name(self, name: String) -> Builder {
Builder(self.0.name(name))
}
pub fn stack_size(self, size: usize) -> Builder {
Builder(self.0.stack_size(size))
}
pub fn spawn<F, T>(self, f: F) -> std::io::Result<JoinHandle<T>>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
self.0.spawn(f).map(JoinHandle::from)
}
}
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
Builder::new().spawn(f).expect("failed to spawn thread")
}
#[test]
fn smoke() {
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
drop(spawn(|| COUNTER.fetch_add(1, Ordering::SeqCst)));
assert_eq!(COUNTER.load(Ordering::SeqCst), 1);
let res = std::panic::catch_unwind(|| {
let _handle = Builder::new()
.name("panicky".to_string())
.spawn(|| COUNTER.fetch_add(1, Ordering::SeqCst))
.unwrap();
panic!("boom")
});
assert!(res.is_err());
assert_eq!(COUNTER.load(Ordering::SeqCst), 2);
let res = std::panic::catch_unwind(|| {
let handle = spawn(|| panic!("boom"));
let () = handle.join();
});
assert!(res.is_err());
let res = std::panic::catch_unwind(|| {
let _handle = spawn(|| panic!("boom"));
});
assert!(res.is_err());
}
|