use super::Builder; use crate::any::Any; use crate::mem; use crate::panic::panic_any; use crate::result; use crate::sync::{ atomic::{AtomicBool, Ordering}, mpsc::{channel, Sender}, Arc, Barrier, }; use crate::thread::{self, Scope, ThreadId}; use crate::time::Duration; use crate::time::Instant; // !!! These tests are dangerous. If something is buggy, they will hang, !!! // !!! instead of exiting cleanly. This might wedge the buildbots. !!! #[test] fn test_unnamed_thread() { thread::spawn(move || { assert!(thread::current().name().is_none()); }) .join() .ok() .expect("thread panicked"); } #[test] fn test_named_thread() { Builder::new() .name("ada lovelace".to_string()) .spawn(move || { assert!(thread::current().name().unwrap() == "ada lovelace".to_string()); }) .unwrap() .join() .unwrap(); } #[cfg(any( // Note: musl didn't add pthread_getname_np until 1.2.3 all(target_os = "linux", target_env = "gnu"), target_os = "macos", target_os = "ios", target_os = "watchos" ))] #[test] fn test_named_thread_truncation() { use crate::ffi::CStr; let long_name = crate::iter::once("test_named_thread_truncation") .chain(crate::iter::repeat(" yada").take(100)) .collect::(); let result = Builder::new().name(long_name.clone()).spawn(move || { // Rust remembers the full thread name itself. assert_eq!(thread::current().name(), Some(long_name.as_str())); // But the system is limited -- make sure we successfully set a truncation. let mut buf = vec![0u8; long_name.len() + 1]; unsafe { libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()); } let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert!(cstr.to_bytes().len() > 0); assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); }); result.unwrap().join().unwrap(); } #[test] #[should_panic] fn test_invalid_named_thread() { let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {}); } #[test] fn test_run_basic() { let (tx, rx) = channel(); thread::spawn(move || { tx.send(()).unwrap(); }); rx.recv().unwrap(); } #[test] fn test_is_finished() { let b = Arc::new(Barrier::new(2)); let t = thread::spawn({ let b = b.clone(); move || { b.wait(); 1234 } }); // Thread is definitely running here, since it's still waiting for the barrier. assert_eq!(t.is_finished(), false); // Unblock the barrier. b.wait(); // Now check that t.is_finished() becomes true within a reasonable time. let start = Instant::now(); while !t.is_finished() { assert!(start.elapsed() < Duration::from_secs(2)); thread::sleep(Duration::from_millis(15)); } // Joining the thread should not block for a significant time now. let join_time = Instant::now(); assert_eq!(t.join().unwrap(), 1234); assert!(join_time.elapsed() < Duration::from_secs(2)); } #[test] fn test_join_panic() { match thread::spawn(move || panic!()).join() { result::Result::Err(_) => (), result::Result::Ok(()) => panic!(), } } #[test] fn test_spawn_sched() { let (tx, rx) = channel(); fn f(i: i32, tx: Sender<()>) { let tx = tx.clone(); thread::spawn(move || { if i == 0 { tx.send(()).unwrap(); } else { f(i - 1, tx); } }); } f(10, tx); rx.recv().unwrap(); } #[test] fn test_spawn_sched_childs_on_default_sched() { let (tx, rx) = channel(); thread::spawn(move || { thread::spawn(move || { tx.send(()).unwrap(); }); }); rx.recv().unwrap(); } fn avoid_copying_the_body(spawnfn: F) where F: FnOnce(Box), { let (tx, rx) = channel(); let x: Box<_> = Box::new(1); let x_in_parent = (&*x) as *const i32 as usize; spawnfn(Box::new(move || { let x_in_child = (&*x) as *const i32 as usize; tx.send(x_in_child).unwrap(); })); let x_in_child = rx.recv().unwrap(); assert_eq!(x_in_parent, x_in_child); } #[test] fn test_avoid_copying_the_body_spawn() { avoid_copying_the_body(|v| { thread::spawn(move || v()); }); } #[test] fn test_avoid_copying_the_body_thread_spawn() { avoid_copying_the_body(|f| { thread::spawn(move || { f(); }); }) } #[test] fn test_avoid_copying_the_body_join() { avoid_copying_the_body(|f| { let _ = thread::spawn(move || f()).join(); }) } #[test] fn test_child_doesnt_ref_parent() { // If the child refcounts the parent thread, this will stack overflow when // climbing the thread tree to dereference each ancestor. (See #1789) // (well, it would if the constant were 8000+ - I lowered it to be more // valgrind-friendly. try this at home, instead..!) const GENERATIONS: u32 = 16; fn child_no(x: u32) -> Box { return Box::new(move || { if x < GENERATIONS { thread::spawn(move || child_no(x + 1)()); } }); } thread::spawn(|| child_no(0)()); } #[test] fn test_simple_newsched_spawn() { thread::spawn(move || {}); } #[test] fn test_try_panic_message_string_literal() { match thread::spawn(move || { panic!("static string"); }) .join() { Err(e) => { type T = &'static str; assert!(e.is::()); assert_eq!(*e.downcast::().unwrap(), "static string"); } Ok(()) => panic!(), } } #[test] fn test_try_panic_any_message_owned_str() { match thread::spawn(move || { panic_any("owned string".to_string()); }) .join() { Err(e) => { type T = String; assert!(e.is::()); assert_eq!(*e.downcast::().unwrap(), "owned string".to_string()); } Ok(()) => panic!(), } } #[test] fn test_try_panic_any_message_any() { match thread::spawn(move || { panic_any(Box::new(413u16) as Box); }) .join() { Err(e) => { type T = Box; assert!(e.is::()); let any = e.downcast::().unwrap(); assert!(any.is::()); assert_eq!(*any.downcast::().unwrap(), 413); } Ok(()) => panic!(), } } #[test] fn test_try_panic_any_message_unit_struct() { struct Juju; match thread::spawn(move || panic_any(Juju)).join() { Err(ref e) if e.is::() => {} Err(_) | Ok(()) => panic!(), } } #[test] fn test_park_unpark_before() { for _ in 0..10 { thread::current().unpark(); thread::park(); } } #[test] fn test_park_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); let _guard = thread::spawn(move || { super::sleep(Duration::from_millis(50)); th.unpark(); }); thread::park(); } } #[test] fn test_park_timeout_unpark_before() { for _ in 0..10 { thread::current().unpark(); thread::park_timeout(Duration::from_millis(u32::MAX as u64)); } } #[test] fn test_park_timeout_unpark_not_called() { for _ in 0..10 { thread::park_timeout(Duration::from_millis(10)); } } #[test] fn test_park_timeout_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); let _guard = thread::spawn(move || { super::sleep(Duration::from_millis(50)); th.unpark(); }); thread::park_timeout(Duration::from_millis(u32::MAX as u64)); } } #[test] fn sleep_ms_smoke() { thread::sleep(Duration::from_millis(2)); } #[test] fn test_size_of_option_thread_id() { assert_eq!(mem::size_of::>(), mem::size_of::()); } #[test] fn test_thread_id_equal() { assert!(thread::current().id() == thread::current().id()); } #[test] fn test_thread_id_not_equal() { let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap(); assert!(thread::current().id() != spawned_id); } #[test] fn test_scoped_threads_drop_result_before_join() { let actually_finished = &AtomicBool::new(false); struct X<'scope, 'env>(&'scope Scope<'scope, 'env>, &'env AtomicBool); impl Drop for X<'_, '_> { fn drop(&mut self) { thread::sleep(Duration::from_millis(20)); let actually_finished = self.1; self.0.spawn(move || { thread::sleep(Duration::from_millis(20)); actually_finished.store(true, Ordering::Relaxed); }); } } thread::scope(|s| { s.spawn(move || { thread::sleep(Duration::from_millis(20)); X(s, actually_finished) }); }); assert!(actually_finished.load(Ordering::Relaxed)); } #[test] fn test_scoped_threads_nll() { // this is mostly a *compilation test* for this exact function: fn foo(x: &u8) { thread::scope(|s| { s.spawn(|| drop(x)); }); } // let's also run it for good measure let x = 42_u8; foo(&x); } // Regression test for https://github.com/rust-lang/rust/issues/98498. #[test] #[cfg(miri)] // relies on Miri's data race detector fn scope_join_race() { for _ in 0..100 { let a_bool = AtomicBool::new(false); thread::scope(|s| { for _ in 0..5 { s.spawn(|| a_bool.load(Ordering::Relaxed)); } for _ in 0..5 { s.spawn(|| a_bool.load(Ordering::Relaxed)); } }); } }