]> git.lizzy.rs Git - rust.git/blob - library/std/src/thread/tests.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / library / std / src / thread / tests.rs
1 use super::Builder;
2 use crate::any::Any;
3 use crate::mem;
4 use crate::panic::panic_any;
5 use crate::result;
6 use crate::sync::{
7     atomic::{AtomicBool, Ordering},
8     mpsc::{channel, Sender},
9     Arc, Barrier,
10 };
11 use crate::thread::{self, Scope, ThreadId};
12 use crate::time::Duration;
13 use crate::time::Instant;
14
15 // !!! These tests are dangerous. If something is buggy, they will hang, !!!
16 // !!! instead of exiting cleanly. This might wedge the buildbots.       !!!
17
18 #[test]
19 fn test_unnamed_thread() {
20     thread::spawn(move || {
21         assert!(thread::current().name().is_none());
22     })
23     .join()
24     .ok()
25     .expect("thread panicked");
26 }
27
28 #[test]
29 fn test_named_thread() {
30     Builder::new()
31         .name("ada lovelace".to_string())
32         .spawn(move || {
33             assert!(thread::current().name().unwrap() == "ada lovelace".to_string());
34         })
35         .unwrap()
36         .join()
37         .unwrap();
38 }
39
40 #[cfg(any(
41     // Note: musl didn't add pthread_getname_np until 1.2.3
42     all(target_os = "linux", target_env = "gnu"),
43     target_os = "macos",
44     target_os = "ios",
45     target_os = "watchos"
46 ))]
47 #[test]
48 fn test_named_thread_truncation() {
49     use crate::ffi::CStr;
50
51     let long_name = crate::iter::once("test_named_thread_truncation")
52         .chain(crate::iter::repeat(" yada").take(100))
53         .collect::<String>();
54
55     let result = Builder::new().name(long_name.clone()).spawn(move || {
56         // Rust remembers the full thread name itself.
57         assert_eq!(thread::current().name(), Some(long_name.as_str()));
58
59         // But the system is limited -- make sure we successfully set a truncation.
60         let mut buf = vec![0u8; long_name.len() + 1];
61         unsafe {
62             libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len());
63         }
64         let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
65         assert!(cstr.to_bytes().len() > 0);
66         assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
67     });
68     result.unwrap().join().unwrap();
69 }
70
71 #[test]
72 #[should_panic]
73 fn test_invalid_named_thread() {
74     let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {});
75 }
76
77 #[test]
78 fn test_run_basic() {
79     let (tx, rx) = channel();
80     thread::spawn(move || {
81         tx.send(()).unwrap();
82     });
83     rx.recv().unwrap();
84 }
85
86 #[test]
87 fn test_is_finished() {
88     let b = Arc::new(Barrier::new(2));
89     let t = thread::spawn({
90         let b = b.clone();
91         move || {
92             b.wait();
93             1234
94         }
95     });
96
97     // Thread is definitely running here, since it's still waiting for the barrier.
98     assert_eq!(t.is_finished(), false);
99
100     // Unblock the barrier.
101     b.wait();
102
103     // Now check that t.is_finished() becomes true within a reasonable time.
104     let start = Instant::now();
105     while !t.is_finished() {
106         assert!(start.elapsed() < Duration::from_secs(2));
107         thread::sleep(Duration::from_millis(15));
108     }
109
110     // Joining the thread should not block for a significant time now.
111     let join_time = Instant::now();
112     assert_eq!(t.join().unwrap(), 1234);
113     assert!(join_time.elapsed() < Duration::from_secs(2));
114 }
115
116 #[test]
117 fn test_join_panic() {
118     match thread::spawn(move || panic!()).join() {
119         result::Result::Err(_) => (),
120         result::Result::Ok(()) => panic!(),
121     }
122 }
123
124 #[test]
125 fn test_spawn_sched() {
126     let (tx, rx) = channel();
127
128     fn f(i: i32, tx: Sender<()>) {
129         let tx = tx.clone();
130         thread::spawn(move || {
131             if i == 0 {
132                 tx.send(()).unwrap();
133             } else {
134                 f(i - 1, tx);
135             }
136         });
137     }
138     f(10, tx);
139     rx.recv().unwrap();
140 }
141
142 #[test]
143 fn test_spawn_sched_childs_on_default_sched() {
144     let (tx, rx) = channel();
145
146     thread::spawn(move || {
147         thread::spawn(move || {
148             tx.send(()).unwrap();
149         });
150     });
151
152     rx.recv().unwrap();
153 }
154
155 fn avoid_copying_the_body<F>(spawnfn: F)
156 where
157     F: FnOnce(Box<dyn Fn() + Send>),
158 {
159     let (tx, rx) = channel();
160
161     let x: Box<_> = Box::new(1);
162     let x_in_parent = (&*x) as *const i32 as usize;
163
164     spawnfn(Box::new(move || {
165         let x_in_child = (&*x) as *const i32 as usize;
166         tx.send(x_in_child).unwrap();
167     }));
168
169     let x_in_child = rx.recv().unwrap();
170     assert_eq!(x_in_parent, x_in_child);
171 }
172
173 #[test]
174 fn test_avoid_copying_the_body_spawn() {
175     avoid_copying_the_body(|v| {
176         thread::spawn(move || v());
177     });
178 }
179
180 #[test]
181 fn test_avoid_copying_the_body_thread_spawn() {
182     avoid_copying_the_body(|f| {
183         thread::spawn(move || {
184             f();
185         });
186     })
187 }
188
189 #[test]
190 fn test_avoid_copying_the_body_join() {
191     avoid_copying_the_body(|f| {
192         let _ = thread::spawn(move || f()).join();
193     })
194 }
195
196 #[test]
197 fn test_child_doesnt_ref_parent() {
198     // If the child refcounts the parent thread, this will stack overflow when
199     // climbing the thread tree to dereference each ancestor. (See #1789)
200     // (well, it would if the constant were 8000+ - I lowered it to be more
201     // valgrind-friendly. try this at home, instead..!)
202     const GENERATIONS: u32 = 16;
203     fn child_no(x: u32) -> Box<dyn Fn() + Send> {
204         return Box::new(move || {
205             if x < GENERATIONS {
206                 thread::spawn(move || child_no(x + 1)());
207             }
208         });
209     }
210     thread::spawn(|| child_no(0)());
211 }
212
213 #[test]
214 fn test_simple_newsched_spawn() {
215     thread::spawn(move || {});
216 }
217
218 #[test]
219 fn test_try_panic_message_string_literal() {
220     match thread::spawn(move || {
221         panic!("static string");
222     })
223     .join()
224     {
225         Err(e) => {
226             type T = &'static str;
227             assert!(e.is::<T>());
228             assert_eq!(*e.downcast::<T>().unwrap(), "static string");
229         }
230         Ok(()) => panic!(),
231     }
232 }
233
234 #[test]
235 fn test_try_panic_any_message_owned_str() {
236     match thread::spawn(move || {
237         panic_any("owned string".to_string());
238     })
239     .join()
240     {
241         Err(e) => {
242             type T = String;
243             assert!(e.is::<T>());
244             assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string());
245         }
246         Ok(()) => panic!(),
247     }
248 }
249
250 #[test]
251 fn test_try_panic_any_message_any() {
252     match thread::spawn(move || {
253         panic_any(Box::new(413u16) as Box<dyn Any + Send>);
254     })
255     .join()
256     {
257         Err(e) => {
258             type T = Box<dyn Any + Send>;
259             assert!(e.is::<T>());
260             let any = e.downcast::<T>().unwrap();
261             assert!(any.is::<u16>());
262             assert_eq!(*any.downcast::<u16>().unwrap(), 413);
263         }
264         Ok(()) => panic!(),
265     }
266 }
267
268 #[test]
269 fn test_try_panic_any_message_unit_struct() {
270     struct Juju;
271
272     match thread::spawn(move || panic_any(Juju)).join() {
273         Err(ref e) if e.is::<Juju>() => {}
274         Err(_) | Ok(()) => panic!(),
275     }
276 }
277
278 #[test]
279 fn test_park_unpark_before() {
280     for _ in 0..10 {
281         thread::current().unpark();
282         thread::park();
283     }
284 }
285
286 #[test]
287 fn test_park_unpark_called_other_thread() {
288     for _ in 0..10 {
289         let th = thread::current();
290
291         let _guard = thread::spawn(move || {
292             super::sleep(Duration::from_millis(50));
293             th.unpark();
294         });
295
296         thread::park();
297     }
298 }
299
300 #[test]
301 fn test_park_timeout_unpark_before() {
302     for _ in 0..10 {
303         thread::current().unpark();
304         thread::park_timeout(Duration::from_millis(u32::MAX as u64));
305     }
306 }
307
308 #[test]
309 fn test_park_timeout_unpark_not_called() {
310     for _ in 0..10 {
311         thread::park_timeout(Duration::from_millis(10));
312     }
313 }
314
315 #[test]
316 fn test_park_timeout_unpark_called_other_thread() {
317     for _ in 0..10 {
318         let th = thread::current();
319
320         let _guard = thread::spawn(move || {
321             super::sleep(Duration::from_millis(50));
322             th.unpark();
323         });
324
325         thread::park_timeout(Duration::from_millis(u32::MAX as u64));
326     }
327 }
328
329 #[test]
330 fn sleep_ms_smoke() {
331     thread::sleep(Duration::from_millis(2));
332 }
333
334 #[test]
335 fn test_size_of_option_thread_id() {
336     assert_eq!(mem::size_of::<Option<ThreadId>>(), mem::size_of::<ThreadId>());
337 }
338
339 #[test]
340 fn test_thread_id_equal() {
341     assert!(thread::current().id() == thread::current().id());
342 }
343
344 #[test]
345 fn test_thread_id_not_equal() {
346     let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap();
347     assert!(thread::current().id() != spawned_id);
348 }
349
350 #[test]
351 fn test_scoped_threads_drop_result_before_join() {
352     let actually_finished = &AtomicBool::new(false);
353     struct X<'scope, 'env>(&'scope Scope<'scope, 'env>, &'env AtomicBool);
354     impl Drop for X<'_, '_> {
355         fn drop(&mut self) {
356             thread::sleep(Duration::from_millis(20));
357             let actually_finished = self.1;
358             self.0.spawn(move || {
359                 thread::sleep(Duration::from_millis(20));
360                 actually_finished.store(true, Ordering::Relaxed);
361             });
362         }
363     }
364     thread::scope(|s| {
365         s.spawn(move || {
366             thread::sleep(Duration::from_millis(20));
367             X(s, actually_finished)
368         });
369     });
370     assert!(actually_finished.load(Ordering::Relaxed));
371 }
372
373 #[test]
374 fn test_scoped_threads_nll() {
375     // this is mostly a *compilation test* for this exact function:
376     fn foo(x: &u8) {
377         thread::scope(|s| {
378             s.spawn(|| drop(x));
379         });
380     }
381     // let's also run it for good measure
382     let x = 42_u8;
383     foo(&x);
384 }
385
386 // Regression test for https://github.com/rust-lang/rust/issues/98498.
387 #[test]
388 #[cfg(miri)] // relies on Miri's data race detector
389 fn scope_join_race() {
390     for _ in 0..100 {
391         let a_bool = AtomicBool::new(false);
392
393         thread::scope(|s| {
394             for _ in 0..5 {
395                 s.spawn(|| a_bool.load(Ordering::Relaxed));
396             }
397
398             for _ in 0..5 {
399                 s.spawn(|| a_bool.load(Ordering::Relaxed));
400             }
401         });
402     }
403 }