1 use std::any::Any;
2 use std::sync::atomic::{AtomicUsize, Ordering};
3 use std::thread::sleep;
4 use std::time::Duration;
5
6 use crossbeam_utils::thread;
7
8 const THREADS: usize = 10;
9 const SMALL_STACK_SIZE: usize = 20;
10
11 #[test]
12 // Android aborts on panic and this test relies on stack unwinding.
13 #[cfg(not(target_os = "android"))]
join()14 fn join() {
15 let counter = AtomicUsize::new(0);
16 thread::scope(|scope| {
17 let handle = scope.spawn(|_| {
18 counter.store(1, Ordering::Relaxed);
19 });
20 assert!(handle.join().is_ok());
21
22 let panic_handle = scope.spawn(|_| {
23 panic!("\"My honey is running out!\", said Pooh.");
24 });
25 assert!(panic_handle.join().is_err());
26 })
27 .unwrap();
28
29 // There should be sufficient synchronization.
30 assert_eq!(1, counter.load(Ordering::Relaxed));
31 }
32
33 #[test]
counter()34 fn counter() {
35 let counter = AtomicUsize::new(0);
36 thread::scope(|scope| {
37 for _ in 0..THREADS {
38 scope.spawn(|_| {
39 counter.fetch_add(1, Ordering::Relaxed);
40 });
41 }
42 })
43 .unwrap();
44
45 assert_eq!(THREADS, counter.load(Ordering::Relaxed));
46 }
47
48 #[test]
counter_builder()49 fn counter_builder() {
50 let counter = AtomicUsize::new(0);
51 thread::scope(|scope| {
52 for i in 0..THREADS {
53 scope
54 .builder()
55 .name(format!("child-{}", i))
56 .stack_size(SMALL_STACK_SIZE)
57 .spawn(|_| {
58 counter.fetch_add(1, Ordering::Relaxed);
59 })
60 .unwrap();
61 }
62 })
63 .unwrap();
64
65 assert_eq!(THREADS, counter.load(Ordering::Relaxed));
66 }
67
68 #[test]
69 // Android aborts on panic and this test relies on stack unwinding.
70 #[cfg(not(target_os = "android"))]
counter_panic()71 fn counter_panic() {
72 let counter = AtomicUsize::new(0);
73 let result = thread::scope(|scope| {
74 scope.spawn(|_| {
75 panic!("\"My honey is running out!\", said Pooh.");
76 });
77 sleep(Duration::from_millis(100));
78
79 for _ in 0..THREADS {
80 scope.spawn(|_| {
81 counter.fetch_add(1, Ordering::Relaxed);
82 });
83 }
84 });
85
86 assert_eq!(THREADS, counter.load(Ordering::Relaxed));
87 assert!(result.is_err());
88 }
89
90 #[test]
91 // Android aborts on panic and this test relies on stack unwinding.
92 #[cfg(not(target_os = "android"))]
panic_twice()93 fn panic_twice() {
94 let result = thread::scope(|scope| {
95 scope.spawn(|_| {
96 sleep(Duration::from_millis(500));
97 panic!("thread #1");
98 });
99 scope.spawn(|_| {
100 panic!("thread #2");
101 });
102 });
103
104 let err = result.unwrap_err();
105 let vec = err
106 .downcast_ref::<Vec<Box<dyn Any + Send + 'static>>>()
107 .unwrap();
108 assert_eq!(2, vec.len());
109
110 let first = vec[0].downcast_ref::<&str>().unwrap();
111 let second = vec[1].downcast_ref::<&str>().unwrap();
112 assert_eq!("thread #1", *first);
113 assert_eq!("thread #2", *second)
114 }
115
116 #[test]
117 // Android aborts on panic and this test relies on stack unwinding.
118 #[cfg(not(target_os = "android"))]
panic_many()119 fn panic_many() {
120 let result = thread::scope(|scope| {
121 scope.spawn(|_| panic!("deliberate panic #1"));
122 scope.spawn(|_| panic!("deliberate panic #2"));
123 scope.spawn(|_| panic!("deliberate panic #3"));
124 });
125
126 let err = result.unwrap_err();
127 let vec = err
128 .downcast_ref::<Vec<Box<dyn Any + Send + 'static>>>()
129 .unwrap();
130 assert_eq!(3, vec.len());
131
132 for panic in vec.iter() {
133 let panic = panic.downcast_ref::<&str>().unwrap();
134 assert!(
135 *panic == "deliberate panic #1"
136 || *panic == "deliberate panic #2"
137 || *panic == "deliberate panic #3"
138 );
139 }
140 }
141
142 #[test]
nesting()143 fn nesting() {
144 let var = "foo".to_string();
145
146 struct Wrapper<'a> {
147 var: &'a String,
148 }
149
150 impl<'a> Wrapper<'a> {
151 fn recurse(&'a self, scope: &thread::Scope<'a>, depth: usize) {
152 assert_eq!(self.var, "foo");
153
154 if depth > 0 {
155 scope.spawn(move |scope| {
156 self.recurse(scope, depth - 1);
157 });
158 }
159 }
160 }
161
162 let wrapper = Wrapper { var: &var };
163
164 thread::scope(|scope| {
165 scope.spawn(|scope| {
166 scope.spawn(|scope| {
167 wrapper.recurse(scope, 5);
168 });
169 });
170 })
171 .unwrap();
172 }
173
174 #[test]
join_nested()175 fn join_nested() {
176 thread::scope(|scope| {
177 scope.spawn(|scope| {
178 let handle = scope.spawn(|_| 7);
179
180 sleep(Duration::from_millis(200));
181 handle.join().unwrap();
182 });
183
184 sleep(Duration::from_millis(100));
185 })
186 .unwrap();
187 }
188
189 #[test]
scope_returns_ok()190 fn scope_returns_ok() {
191 let result = thread::scope(|scope| scope.spawn(|_| 1234).join().unwrap()).unwrap();
192 assert_eq!(result, 1234);
193 }
194
195 #[cfg(unix)]
196 #[test]
as_pthread_t()197 fn as_pthread_t() {
198 use std::os::unix::thread::JoinHandleExt;
199 thread::scope(|scope| {
200 let handle = scope.spawn(|_scope| {
201 sleep(Duration::from_millis(100));
202 42
203 });
204 let _pthread_t = handle.as_pthread_t();
205 handle.join().unwrap();
206 })
207 .unwrap();
208 }
209
210 #[cfg(windows)]
211 #[test]
as_raw_handle()212 fn as_raw_handle() {
213 use std::os::windows::io::AsRawHandle;
214 thread::scope(|scope| {
215 let handle = scope.spawn(|_scope| {
216 sleep(Duration::from_millis(100));
217 42
218 });
219 let _raw_handle = handle.as_raw_handle();
220 handle.join().unwrap();
221 })
222 .unwrap();
223 }
224