1 use crate::scope;
2 use std::any::Any;
3 use std::sync::mpsc::channel;
4 use std::sync::Mutex;
5 
6 use super::{spawn, spawn_fifo};
7 use crate::ThreadPoolBuilder;
8 
9 #[test]
spawn_then_join_in_worker()10 fn spawn_then_join_in_worker() {
11     let (tx, rx) = channel();
12     scope(move |_| {
13         spawn(move || tx.send(22).unwrap());
14     });
15     assert_eq!(22, rx.recv().unwrap());
16 }
17 
18 #[test]
spawn_then_join_outside_worker()19 fn spawn_then_join_outside_worker() {
20     let (tx, rx) = channel();
21     spawn(move || tx.send(22).unwrap());
22     assert_eq!(22, rx.recv().unwrap());
23 }
24 
25 #[test]
panic_fwd()26 fn panic_fwd() {
27     let (tx, rx) = channel();
28 
29     let tx = Mutex::new(tx);
30     let panic_handler = move |err: Box<dyn Any + Send>| {
31         let tx = tx.lock().unwrap();
32         if let Some(&msg) = err.downcast_ref::<&str>() {
33             if msg == "Hello, world!" {
34                 tx.send(1).unwrap();
35             } else {
36                 tx.send(2).unwrap();
37             }
38         } else {
39             tx.send(3).unwrap();
40         }
41     };
42 
43     let builder = ThreadPoolBuilder::new().panic_handler(panic_handler);
44 
45     builder
46         .build()
47         .unwrap()
48         .spawn(move || panic!("Hello, world!"));
49 
50     assert_eq!(1, rx.recv().unwrap());
51 }
52 
53 /// Test what happens when the thread-pool is dropped but there are
54 /// still active asynchronous tasks. We expect the thread-pool to stay
55 /// alive and executing until those threads are complete.
56 #[test]
termination_while_things_are_executing()57 fn termination_while_things_are_executing() {
58     let (tx0, rx0) = channel();
59     let (tx1, rx1) = channel();
60 
61     // Create a thread-pool and spawn some code in it, but then drop
62     // our reference to it.
63     {
64         let thread_pool = ThreadPoolBuilder::new().build().unwrap();
65         thread_pool.spawn(move || {
66             let data = rx0.recv().unwrap();
67 
68             // At this point, we know the "main" reference to the
69             // `ThreadPool` has been dropped, but there are still
70             // active threads. Launch one more.
71             spawn(move || {
72                 tx1.send(data).unwrap();
73             });
74         });
75     }
76 
77     tx0.send(22).unwrap();
78     let v = rx1.recv().unwrap();
79     assert_eq!(v, 22);
80 }
81 
82 #[test]
custom_panic_handler_and_spawn()83 fn custom_panic_handler_and_spawn() {
84     let (tx, rx) = channel();
85 
86     // Create a parallel closure that will send panics on the
87     // channel; since the closure is potentially executed in parallel
88     // with itself, we have to wrap `tx` in a mutex.
89     let tx = Mutex::new(tx);
90     let panic_handler = move |e: Box<dyn Any + Send>| {
91         tx.lock().unwrap().send(e).unwrap();
92     };
93 
94     // Execute an async that will panic.
95     let builder = ThreadPoolBuilder::new().panic_handler(panic_handler);
96     builder.build().unwrap().spawn(move || {
97         panic!("Hello, world!");
98     });
99 
100     // Check that we got back the panic we expected.
101     let error = rx.recv().unwrap();
102     if let Some(&msg) = error.downcast_ref::<&str>() {
103         assert_eq!(msg, "Hello, world!");
104     } else {
105         panic!("did not receive a string from panic handler");
106     }
107 }
108 
109 #[test]
custom_panic_handler_and_nested_spawn()110 fn custom_panic_handler_and_nested_spawn() {
111     let (tx, rx) = channel();
112 
113     // Create a parallel closure that will send panics on the
114     // channel; since the closure is potentially executed in parallel
115     // with itself, we have to wrap `tx` in a mutex.
116     let tx = Mutex::new(tx);
117     let panic_handler = move |e| {
118         tx.lock().unwrap().send(e).unwrap();
119     };
120 
121     // Execute an async that will (eventually) panic.
122     const PANICS: usize = 3;
123     let builder = ThreadPoolBuilder::new().panic_handler(panic_handler);
124     builder.build().unwrap().spawn(move || {
125         // launch 3 nested spawn-asyncs; these should be in the same
126         // thread-pool and hence inherit the same panic handler
127         for _ in 0..PANICS {
128             spawn(move || {
129                 panic!("Hello, world!");
130             });
131         }
132     });
133 
134     // Check that we get back the panics we expected.
135     for _ in 0..PANICS {
136         let error = rx.recv().unwrap();
137         if let Some(&msg) = error.downcast_ref::<&str>() {
138             assert_eq!(msg, "Hello, world!");
139         } else {
140             panic!("did not receive a string from panic handler");
141         }
142     }
143 }
144 
145 macro_rules! test_order {
146     ($outer_spawn:ident, $inner_spawn:ident) => {{
147         let builder = ThreadPoolBuilder::new().num_threads(1);
148         let pool = builder.build().unwrap();
149         let (tx, rx) = channel();
150         pool.install(move || {
151             for i in 0..10 {
152                 let tx = tx.clone();
153                 $outer_spawn(move || {
154                     for j in 0..10 {
155                         let tx = tx.clone();
156                         $inner_spawn(move || {
157                             tx.send(i * 10 + j).unwrap();
158                         });
159                     }
160                 });
161             }
162         });
163         rx.iter().collect::<Vec<i32>>()
164     }};
165 }
166 
167 #[test]
lifo_order()168 fn lifo_order() {
169     // In the absense of stealing, `spawn()` jobs on a thread will run in LIFO order.
170     let vec = test_order!(spawn, spawn);
171     let expected: Vec<i32> = (0..100).rev().collect(); // LIFO -> reversed
172     assert_eq!(vec, expected);
173 }
174 
175 #[test]
fifo_order()176 fn fifo_order() {
177     // In the absense of stealing, `spawn_fifo()` jobs on a thread will run in FIFO order.
178     let vec = test_order!(spawn_fifo, spawn_fifo);
179     let expected: Vec<i32> = (0..100).collect(); // FIFO -> natural order
180     assert_eq!(vec, expected);
181 }
182 
183 #[test]
lifo_fifo_order()184 fn lifo_fifo_order() {
185     // LIFO on the outside, FIFO on the inside
186     let vec = test_order!(spawn, spawn_fifo);
187     let expected: Vec<i32> = (0..10)
188         .rev()
189         .flat_map(|i| (0..10).map(move |j| i * 10 + j))
190         .collect();
191     assert_eq!(vec, expected);
192 }
193 
194 #[test]
fifo_lifo_order()195 fn fifo_lifo_order() {
196     // FIFO on the outside, LIFO on the inside
197     let vec = test_order!(spawn_fifo, spawn);
198     let expected: Vec<i32> = (0..10)
199         .flat_map(|i| (0..10).rev().map(move |j| i * 10 + j))
200         .collect();
201     assert_eq!(vec, expected);
202 }
203 
204 macro_rules! spawn_send {
205     ($spawn:ident, $tx:ident, $i:expr) => {{
206         let tx = $tx.clone();
207         $spawn(move || tx.send($i).unwrap());
208     }};
209 }
210 
211 /// Test mixed spawns pushing a series of numbers, interleaved such
212 /// such that negative values are using the second kind of spawn.
213 macro_rules! test_mixed_order {
214     ($pos_spawn:ident, $neg_spawn:ident) => {{
215         let builder = ThreadPoolBuilder::new().num_threads(1);
216         let pool = builder.build().unwrap();
217         let (tx, rx) = channel();
218         pool.install(move || {
219             spawn_send!($pos_spawn, tx, 0);
220             spawn_send!($neg_spawn, tx, -1);
221             spawn_send!($pos_spawn, tx, 1);
222             spawn_send!($neg_spawn, tx, -2);
223             spawn_send!($pos_spawn, tx, 2);
224             spawn_send!($neg_spawn, tx, -3);
225             spawn_send!($pos_spawn, tx, 3);
226         });
227         rx.iter().collect::<Vec<i32>>()
228     }};
229 }
230 
231 #[test]
mixed_lifo_fifo_order()232 fn mixed_lifo_fifo_order() {
233     let vec = test_mixed_order!(spawn, spawn_fifo);
234     let expected = vec![3, -1, 2, -2, 1, -3, 0];
235     assert_eq!(vec, expected);
236 }
237 
238 #[test]
mixed_fifo_lifo_order()239 fn mixed_fifo_lifo_order() {
240     let vec = test_mixed_order!(spawn_fifo, spawn);
241     let expected = vec![0, -3, 1, -2, 2, -1, 3];
242     assert_eq!(vec, expected);
243 }
244