diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 63cf6b62145a9..4bec02bed2eb6 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -338,7 +338,9 @@ extern crate backtrace_sys; // would generate duplicate lang item errors), and any globals it defines are // _not_ the globals used by "real" std. So this import, defined only during // testing gives test-std access to real-std lang items and globals. See #2912 -#[cfg(test)] extern crate std as realstd; +#[cfg(test)] +#[macro_use] +extern crate std as realstd; #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] #[macro_use] @@ -442,7 +444,13 @@ pub mod f32; pub mod f64; #[macro_use] +#[cfg(not(test))] pub mod thread; +#[cfg(test)] +pub use realstd::thread; +#[cfg(test)] +#[path = "thread/tests.rs"] +mod thread_tests; pub mod ascii; pub mod collections; pub mod env; diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index aaffc9bad4547..fa909461d6291 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -300,6 +300,7 @@ pub unsafe fn try R>(f: F) -> Result> { } /// Determines whether the current thread is unwinding because of panic. +#[cfg(not(test))] pub fn panicking() -> bool { update_panic_count(0) != 0 } diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs index 950420420f09e..0894c59aa0f73 100644 --- a/src/libstd/sys/cloudabi/thread.rs +++ b/src/libstd/sys/cloudabi/thread.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(dead_code))] + use boxed::FnBox; use cmp; use ffi::CStr; diff --git a/src/libstd/sys/redox/ext/mod.rs b/src/libstd/sys/redox/ext/mod.rs index 8a2d243c7ff62..ab81c88026dc4 100644 --- a/src/libstd/sys/redox/ext/mod.rs +++ b/src/libstd/sys/redox/ext/mod.rs @@ -25,7 +25,10 @@ pub mod fs; pub mod io; pub mod net; pub mod process; +#[cfg(not(test))] pub mod thread; +#[cfg(test)] +pub use realstd::os::redox::thread; /// A prelude for conveniently writing platform-specific code. /// diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs index 18679a7871e24..62617276320c3 100644 --- a/src/libstd/sys/redox/thread.rs +++ b/src/libstd/sys/redox/thread.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(dead_code))] + use boxed::FnBox; use ffi::CStr; use io; diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs index e1fc369684527..c32c21fcbcbef 100644 --- a/src/libstd/sys/sgx/abi/tls.rs +++ b/src/libstd/sys/sgx/abi/tls.rs @@ -10,45 +10,14 @@ const USIZE_BITS: usize = 64; const TLS_KEYS: usize = 128; // Same as POSIX minimum const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; +#[cfg(not(test))] static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; macro_rules! dup { ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); (() $($val:tt)*) => ([$($val),*]) } -static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = [ - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), -]; +#[cfg(not(test))] +static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); extern "C" { fn get_tls_ptr() -> *const u8; @@ -86,6 +55,7 @@ pub struct ActiveTls<'a> { tls: &'a Tls } +#[cfg(not(test))] impl<'a> Drop for ActiveTls<'a> { fn drop(&mut self) { let value_with_destructor = |key: usize| { @@ -127,7 +97,10 @@ impl Tls { unsafe fn current<'a>() -> &'a Tls { &*(get_tls_ptr() as *const Tls) } +} +#[cfg(not(test))] +impl Tls { pub fn create(dtor: Option) -> Key { let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded"); TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); @@ -151,6 +124,25 @@ impl Tls { } } +#[cfg(test)] +impl Tls { + pub fn create(_dtor: Option) -> Key { + rtabort!("Calling cfg-test version of thread-local storage internals") + } + + pub fn set(_key: Key, _value: *mut u8) { + rtabort!("Calling cfg-test version of thread-local storage internals") + } + + pub fn get(_key: Key) -> *mut u8 { + rtabort!("Calling cfg-test version of thread-local storage internals") + } + + pub fn destroy(_key: Key) { + rtabort!("Calling cfg-test version of thread-local storage internals") + } +} + mod sync_bitset { use sync::atomic::{AtomicUsize, Ordering}; use iter::{Enumerate, Peekable}; diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index b4bdb03e61af6..467f8f9e2a6a5 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(dead_code))] + use boxed::FnBox; use ffi::CStr; use io; @@ -10,8 +12,7 @@ pub struct Thread(task_queue::JoinHandle); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; mod task_queue { - use sync::{Mutex, MutexGuard, Once}; - use sync::mpsc; + use sync::{MutexGuard, mpsc}; use boxed::FnBox; pub type JoinHandle = mpsc::Receiver<()>; @@ -33,15 +34,23 @@ mod task_queue { } } - static TASK_QUEUE_INIT: Once = Once::new(); - static mut TASK_QUEUE: Option>> = None; - + #[cfg(not(test))] pub(super) fn lock() -> MutexGuard<'static, Vec> { + use sync::{Mutex, Once}; + + static TASK_QUEUE_INIT: Once = Once::new(); + static mut TASK_QUEUE: Option>> = None; + unsafe { TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default()) ); TASK_QUEUE.as_ref().unwrap().lock().unwrap() } } + + #[cfg(test)] + pub(super) fn lock() -> MutexGuard<'static, Vec> { + rtabort!("Calling cfg-test version of threading internals") + } } impl Thread { diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index ccbac1a3e4b59..cdcd8f3a14a4f 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -32,7 +32,10 @@ pub mod ffi; pub mod fs; pub mod process; pub mod raw; +#[cfg(not(test))] pub mod thread; +#[cfg(test)] +pub use realstd::os::unix::thread; pub mod net; /// A prelude for conveniently writing platform-specific code. diff --git a/src/libstd/sys/unix/fast_thread_local.rs b/src/libstd/sys/unix/fast_thread_local.rs index 742ffd12b883d..40a6443f324a2 100644 --- a/src/libstd/sys/unix/fast_thread_local.rs +++ b/src/libstd/sys/unix/fast_thread_local.rs @@ -1,3 +1,4 @@ +#![cfg_attr(test, allow(dead_code))] #![cfg(target_thread_local)] #![unstable(feature = "thread_local_internals", issue = "0")] diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index e29557f1ba29b..6b5d6ab98a5fd 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(dead_code))] + use boxed::FnBox; use cmp; use ffi::CStr; diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs index c2322088e8e5a..2481c9a7d3d8d 100644 --- a/src/libstd/sys/wasm/thread.rs +++ b/src/libstd/sys/wasm/thread.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(dead_code))] + use boxed::FnBox; use ffi::CStr; use io; diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs index 0a6d435d3290e..44bc0b8c1f54f 100644 --- a/src/libstd/sys/windows/ext/mod.rs +++ b/src/libstd/sys/windows/ext/mod.rs @@ -15,7 +15,10 @@ pub mod fs; pub mod io; pub mod raw; pub mod process; +#[cfg(not(test))] pub mod thread; +#[cfg(test)] +pub use realstd::os::windows::thread; /// A prelude for conveniently writing platform-specific code. /// diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index bd7cb673a0dbe..bef8ca9da9a7c 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(dead_code))] + use boxed::FnBox; use io; use ffi::CStr; diff --git a/src/libstd/sys_common/thread.rs b/src/libstd/sys_common/thread.rs index fe9ad7623b7d5..0f7518a614b98 100644 --- a/src/libstd/sys_common/thread.rs +++ b/src/libstd/sys_common/thread.rs @@ -1,10 +1,11 @@ +#![allow(dead_code)] + use boxed::FnBox; use env; use sync::atomic::{self, Ordering}; use sys::stack_overflow; use sys::thread as imp; -#[allow(dead_code)] pub unsafe fn start_thread(main: *mut u8) { // Next, set up our stack overflow handler which may get triggered if we run // out of stack. diff --git a/src/libstd/sys_common/thread_info.rs b/src/libstd/sys_common/thread_info.rs index b4bca72b09d1b..766f559b33e6c 100644 --- a/src/libstd/sys_common/thread_info.rs +++ b/src/libstd/sys_common/thread_info.rs @@ -12,6 +12,7 @@ struct ThreadInfo { thread_local! { static THREAD_INFO: RefCell> = RefCell::new(None) } impl ThreadInfo { + #[cfg(not(test))] fn with(f: F) -> Option where F: FnOnce(&mut ThreadInfo) -> R { THREAD_INFO.try_with(move |c| { if c.borrow().is_none() { @@ -23,6 +24,11 @@ impl ThreadInfo { f(c.borrow_mut().as_mut().unwrap()) }).ok() } + + #[cfg(test)] + fn with(_f: F) -> Option where F: FnOnce(&mut ThreadInfo) -> R { + None + } } pub fn current_thread() -> Option { diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 8207709e1f9f0..415d5add0d680 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -481,192 +481,3 @@ pub mod os { key.os.set(ptr::null_mut()); } } - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use sync::mpsc::{channel, Sender}; - use cell::{Cell, UnsafeCell}; - use thread; - - struct Foo(Sender<()>); - - impl Drop for Foo { - fn drop(&mut self) { - let Foo(ref s) = *self; - s.send(()).unwrap(); - } - } - - #[test] - fn smoke_no_dtor() { - thread_local!(static FOO: Cell = Cell::new(1)); - - FOO.with(|f| { - assert_eq!(f.get(), 1); - f.set(2); - }); - let (tx, rx) = channel(); - let _t = thread::spawn(move|| { - FOO.with(|f| { - assert_eq!(f.get(), 1); - }); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); - - FOO.with(|f| { - assert_eq!(f.get(), 2); - }); - } - - #[test] - fn states() { - struct Foo; - impl Drop for Foo { - fn drop(&mut self) { - assert!(FOO.try_with(|_| ()).is_err()); - } - } - thread_local!(static FOO: Foo = Foo); - - thread::spawn(|| { - assert!(FOO.try_with(|_| ()).is_ok()); - }).join().ok().unwrap(); - } - - #[test] - fn smoke_dtor() { - thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move|| unsafe { - let mut tx = Some(tx); - FOO.with(|f| { - *f.get() = Some(Foo(tx.take().unwrap())); - }); - }); - rx.recv().unwrap(); - } - - #[test] - fn circular() { - struct S1; - struct S2; - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - static mut HITS: u32 = 0; - - impl Drop for S1 { - fn drop(&mut self) { - unsafe { - HITS += 1; - if K2.try_with(|_| ()).is_err() { - assert_eq!(HITS, 3); - } else { - if HITS == 1 { - K2.with(|s| *s.get() = Some(S2)); - } else { - assert_eq!(HITS, 3); - } - } - } - } - } - impl Drop for S2 { - fn drop(&mut self) { - unsafe { - HITS += 1; - assert!(K1.try_with(|_| ()).is_ok()); - assert_eq!(HITS, 2); - K1.with(|s| *s.get() = Some(S1)); - } - } - } - - thread::spawn(move|| { - drop(S1); - }).join().ok().unwrap(); - } - - #[test] - fn self_referential() { - struct S1; - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - - impl Drop for S1 { - fn drop(&mut self) { - assert!(K1.try_with(|_| ()).is_err()); - } - } - - thread::spawn(move|| unsafe { - K1.with(|s| *s.get() = Some(S1)); - }).join().ok().unwrap(); - } - - // Note that this test will deadlock if TLS destructors aren't run (this - // requires the destructor to be run to pass the test). - #[test] - fn dtors_in_dtors_in_dtors() { - struct S1(Sender<()>); - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - - impl Drop for S1 { - fn drop(&mut self) { - let S1(ref tx) = *self; - unsafe { - let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); - } - } - } - - let (tx, rx) = channel(); - let _t = thread::spawn(move|| unsafe { - let mut tx = Some(tx); - K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); - }); - rx.recv().unwrap(); - } -} - -#[cfg(test)] -mod dynamic_tests { - use cell::RefCell; - use collections::HashMap; - - #[test] - fn smoke() { - fn square(i: i32) -> i32 { i * i } - thread_local!(static FOO: i32 = square(3)); - - FOO.with(|f| { - assert_eq!(*f, 9); - }); - } - - #[test] - fn hashmap() { - fn map() -> RefCell> { - let mut m = HashMap::new(); - m.insert(1, 2); - RefCell::new(m) - } - thread_local!(static FOO: RefCell> = map()); - - FOO.with(|map| { - assert_eq!(map.borrow()[&1], 2); - }); - } - - #[test] - fn refcell_vec() { - thread_local!(static FOO: RefCell> = RefCell::new(vec![1, 2, 3])); - - FOO.with(|vec| { - assert_eq!(vec.borrow().len(), 3); - vec.borrow_mut().push(4); - assert_eq!(vec.borrow()[3], 4); - }); - } -} diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 438ea3aa3f6a3..115185815f2ce 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -1475,258 +1475,3 @@ fn _assert_sync_and_send() { _assert_both::>(); _assert_both::(); } - -//////////////////////////////////////////////////////////////////////////////// -// Tests -//////////////////////////////////////////////////////////////////////////////// - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use any::Any; - use sync::mpsc::{channel, Sender}; - use result; - use super::{Builder}; - use thread; - use time::Duration; - use u32; - - // !!! 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().unwrap(); - } - - #[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(); - } - - #[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_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 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_static_str() { - 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_message_owned_str() { - match thread::spawn(move|| { - panic!("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_message_any() { - match thread::spawn(move|| { - panic!(box 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_message_unit_struct() { - struct Juju; - - match thread::spawn(move|| { - panic!(Juju) - }).join() { - Err(ref e) if e.is::() => {} - Err(_) | Ok(()) => panic!() - } - } - - #[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_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); - } - - // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due - // to the test harness apparently interfering with stderr configuration. -} diff --git a/src/libstd/thread/tests.rs b/src/libstd/thread/tests.rs new file mode 100644 index 0000000000000..8be671c4b6339 --- /dev/null +++ b/src/libstd/thread/tests.rs @@ -0,0 +1,437 @@ +#[cfg(not(target_os = "emscripten"))] +mod thread { + use any::Any; + use sync::mpsc::{channel, Sender}; + use result; + use thread::{self, Builder}; + use time::Duration; + use u32; + + // !!! 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().unwrap(); + } + + #[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(); + } + + #[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_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 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_static_str() { + 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_message_owned_str() { + match thread::spawn(move|| { + panic!("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_message_any() { + match thread::spawn(move|| { + panic!(box 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_message_unit_struct() { + struct Juju; + + match thread::spawn(move|| { + panic!(Juju) + }).join() { + Err(ref e) if e.is::() => {} + Err(_) | Ok(()) => panic!() + } + } + + #[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 || { + thread::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_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); + } + + // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due + // to the test harness apparently interfering with stderr configuration. +} + +#[cfg(not(target_os = "emscripten"))] +mod local { + use sync::mpsc::{channel, Sender}; + use cell::{Cell, UnsafeCell}; + use thread; + + struct Foo(Sender<()>); + + impl Drop for Foo { + fn drop(&mut self) { + let Foo(ref s) = *self; + s.send(()).unwrap(); + } + } + + #[test] + fn smoke_no_dtor() { + thread_local!(static FOO: Cell = Cell::new(1)); + + FOO.with(|f| { + assert_eq!(f.get(), 1); + f.set(2); + }); + let (tx, rx) = channel(); + let _t = thread::spawn(move|| { + FOO.with(|f| { + assert_eq!(f.get(), 1); + }); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); + + FOO.with(|f| { + assert_eq!(f.get(), 2); + }); + } + + #[test] + fn states() { + struct Foo; + impl Drop for Foo { + fn drop(&mut self) { + assert!(FOO.try_with(|_| ()).is_err()); + } + } + thread_local!(static FOO: Foo = Foo); + + thread::spawn(|| { + assert!(FOO.try_with(|_| ()).is_ok()); + }).join().ok().unwrap(); + } + + #[test] + fn smoke_dtor() { + thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); + + let (tx, rx) = channel(); + let _t = thread::spawn(move|| unsafe { + let mut tx = Some(tx); + FOO.with(|f| { + *f.get() = Some(Foo(tx.take().unwrap())); + }); + }); + rx.recv().unwrap(); + } + + #[test] + fn circular() { + struct S1; + struct S2; + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); + static mut HITS: u32 = 0; + + impl Drop for S1 { + fn drop(&mut self) { + unsafe { + HITS += 1; + if K2.try_with(|_| ()).is_err() { + assert_eq!(HITS, 3); + } else { + if HITS == 1 { + K2.with(|s| *s.get() = Some(S2)); + } else { + assert_eq!(HITS, 3); + } + } + } + } + } + impl Drop for S2 { + fn drop(&mut self) { + unsafe { + HITS += 1; + assert!(K1.try_with(|_| ()).is_ok()); + assert_eq!(HITS, 2); + K1.with(|s| *s.get() = Some(S1)); + } + } + } + + thread::spawn(move|| { + drop(S1); + }).join().ok().unwrap(); + } + + #[test] + fn self_referential() { + struct S1; + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + + impl Drop for S1 { + fn drop(&mut self) { + assert!(K1.try_with(|_| ()).is_err()); + } + } + + thread::spawn(move|| unsafe { + K1.with(|s| *s.get() = Some(S1)); + }).join().ok().unwrap(); + } + + // Note that this test will deadlock if TLS destructors aren't run (this + // requires the destructor to be run to pass the test). + #[test] + fn dtors_in_dtors_in_dtors() { + struct S1(Sender<()>); + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref tx) = *self; + unsafe { + let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); + } + } + } + + let (tx, rx) = channel(); + let _t = thread::spawn(move|| unsafe { + let mut tx = Some(tx); + K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + }); + rx.recv().unwrap(); + } +} + +mod local_dynamic { + use cell::RefCell; + use collections::HashMap; + + #[test] + fn smoke() { + fn square(i: i32) -> i32 { i * i } + thread_local!(static FOO: i32 = square(3)); + + FOO.with(|f| { + assert_eq!(*f, 9); + }); + } + + #[test] + fn hashmap() { + fn map() -> RefCell> { + let mut m = HashMap::new(); + m.insert(1, 2); + RefCell::new(m) + } + thread_local!(static FOO: RefCell> = map()); + + FOO.with(|map| { + assert_eq!(map.borrow()[&1], 2); + }); + } + + #[test] + fn refcell_vec() { + thread_local!(static FOO: RefCell> = RefCell::new(vec![1, 2, 3])); + + FOO.with(|vec| { + assert_eq!(vec.borrow().len(), 3); + vec.borrow_mut().push(4); + assert_eq!(vec.borrow()[3], 4); + }); + } +} diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index ed2218f09d26b..fa9f5fcfe3ca5 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -56,9 +56,11 @@ const EXCEPTION_PATHS: &[&str] = &[ "src/libstd/tests/run-time-detect.rs" , "src/libstd/sys_common/mod.rs", "src/libstd/sys_common/net.rs", + "src/libstd/thread/tests.rs", + "src/libstd/sync/mpsc", // some tests are only run on non-emscripten + "src/libterm", // Not sure how to make this crate portable, but test crate needs it. "src/libtest", // Probably should defer to unstable `std::sys` APIs. - "src/libstd/sync/mpsc", // some tests are only run on non-emscripten // std testing crates, okay for now at least "src/libcore/tests",