2020-08-27 13:45:01 +00:00
|
|
|
use super::Builder;
|
|
|
|
|
use crate::any::Any;
|
|
|
|
|
use crate::mem;
|
2021-12-17 20:08:36 +11:00
|
|
|
use crate::panic::panic_any;
|
2020-08-27 13:45:01 +00:00
|
|
|
use crate::result;
|
2021-10-31 15:02:49 +01:00
|
|
|
use crate::sync::{
|
2022-03-09 11:47:53 +01:00
|
|
|
atomic::{AtomicBool, Ordering},
|
2021-10-31 15:02:49 +01:00
|
|
|
mpsc::{channel, Sender},
|
|
|
|
|
Arc, Barrier,
|
|
|
|
|
};
|
2022-03-09 11:47:53 +01:00
|
|
|
use crate::thread::{self, Scope, ThreadId};
|
2020-08-27 13:45:01 +00:00
|
|
|
use crate::time::Duration;
|
2021-10-31 15:02:49 +01:00
|
|
|
use crate::time::Instant;
|
2020-08-27 13:45:01 +00:00
|
|
|
|
|
|
|
|
// !!! 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();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-21 18:13:22 -07:00
|
|
|
#[cfg(any(target_os = "linux", 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::<String>();
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-27 13:45:01 +00:00
|
|
|
#[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();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-31 15:02:49 +01:00
|
|
|
#[test]
|
2022-03-03 11:24:20 +01:00
|
|
|
fn test_is_finished() {
|
2021-10-31 15:02:49 +01:00
|
|
|
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.
|
2022-03-03 11:24:20 +01:00
|
|
|
assert_eq!(t.is_finished(), false);
|
2021-10-31 15:02:49 +01:00
|
|
|
|
|
|
|
|
// Unblock the barrier.
|
|
|
|
|
b.wait();
|
|
|
|
|
|
2022-03-03 11:24:20 +01:00
|
|
|
// Now check that t.is_finished() becomes true within a reasonable time.
|
2021-10-31 15:02:49 +01:00
|
|
|
let start = Instant::now();
|
2022-03-03 11:24:20 +01:00
|
|
|
while !t.is_finished() {
|
2021-10-31 15:02:49 +01:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-27 13:45:01 +00:00
|
|
|
#[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<F>(spawnfn: F)
|
|
|
|
|
where
|
|
|
|
|
F: FnOnce(Box<dyn Fn() + Send>),
|
|
|
|
|
{
|
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
|
2022-05-29 00:25:32 +02:00
|
|
|
let x: Box<_> = Box::new(1);
|
2020-08-27 13:45:01 +00:00
|
|
|
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<dyn Fn() + Send> {
|
|
|
|
|
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]
|
2021-12-17 20:08:36 +11:00
|
|
|
fn test_try_panic_message_string_literal() {
|
2020-08-27 13:45:01 +00:00
|
|
|
match thread::spawn(move || {
|
|
|
|
|
panic!("static string");
|
|
|
|
|
})
|
|
|
|
|
.join()
|
|
|
|
|
{
|
|
|
|
|
Err(e) => {
|
|
|
|
|
type T = &'static str;
|
|
|
|
|
assert!(e.is::<T>());
|
|
|
|
|
assert_eq!(*e.downcast::<T>().unwrap(), "static string");
|
|
|
|
|
}
|
|
|
|
|
Ok(()) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-12-17 20:08:36 +11:00
|
|
|
fn test_try_panic_any_message_owned_str() {
|
2020-08-27 13:45:01 +00:00
|
|
|
match thread::spawn(move || {
|
2021-12-17 20:08:36 +11:00
|
|
|
panic_any("owned string".to_string());
|
2020-08-27 13:45:01 +00:00
|
|
|
})
|
|
|
|
|
.join()
|
|
|
|
|
{
|
|
|
|
|
Err(e) => {
|
|
|
|
|
type T = String;
|
|
|
|
|
assert!(e.is::<T>());
|
|
|
|
|
assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string());
|
|
|
|
|
}
|
|
|
|
|
Ok(()) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-12-17 20:08:36 +11:00
|
|
|
fn test_try_panic_any_message_any() {
|
2020-08-27 13:45:01 +00:00
|
|
|
match thread::spawn(move || {
|
2022-05-29 00:25:32 +02:00
|
|
|
panic_any(Box::new(413u16) as Box<dyn Any + Send>);
|
2020-08-27 13:45:01 +00:00
|
|
|
})
|
|
|
|
|
.join()
|
|
|
|
|
{
|
|
|
|
|
Err(e) => {
|
|
|
|
|
type T = Box<dyn Any + Send>;
|
|
|
|
|
assert!(e.is::<T>());
|
|
|
|
|
let any = e.downcast::<T>().unwrap();
|
|
|
|
|
assert!(any.is::<u16>());
|
|
|
|
|
assert_eq!(*any.downcast::<u16>().unwrap(), 413);
|
|
|
|
|
}
|
|
|
|
|
Ok(()) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-12-17 20:08:36 +11:00
|
|
|
fn test_try_panic_any_message_unit_struct() {
|
2020-08-27 13:45:01 +00:00
|
|
|
struct Juju;
|
|
|
|
|
|
2021-12-17 20:08:36 +11:00
|
|
|
match thread::spawn(move || panic_any(Juju)).join() {
|
2020-08-27 13:45:01 +00:00
|
|
|
Err(ref e) if e.is::<Juju>() => {}
|
|
|
|
|
Err(_) | Ok(()) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 22:46:47 +02:00
|
|
|
#[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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-27 13:45:01 +00:00
|
|
|
#[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::<Option<ThreadId>>(), mem::size_of::<ThreadId>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-09 11:47:53 +01:00
|
|
|
#[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));
|
|
|
|
|
}
|
2022-06-15 03:12:07 +02:00
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
}
|
2022-08-04 09:01:00 -04:00
|
|
|
|
|
|
|
|
// 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));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|