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