//! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics. #![cfg(not(miri))] // Miri detects that this test is buggy: the destructor of `FOO` uses `std::thread::current()`! use std::thread; use std::time::Duration; use crossbeam_channel::{select, unbounded}; use crossbeam_utils::thread::scope; fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] #[cfg_attr(target_os = "macos", ignore = "TLS is destroyed too early on macOS")] fn use_while_exiting() { struct Foo; impl Drop for Foo { fn drop(&mut self) { // A blocking operation after the thread-locals have been dropped. This will attempt to // use the thread-locals and must not panic. let (_s, r) = unbounded::<()>(); select! { recv(r) -> _ => {} default(ms(100)) => {} } } } thread_local! { static FOO: Foo = Foo; } let (s, r) = unbounded::<()>(); scope(|scope| { scope.spawn(|_| { // First initialize `FOO`, then the thread-locals related to crossbeam-channel. FOO.with(|_| ()); r.recv().unwrap(); // At thread exit, thread-locals related to crossbeam-channel get dropped first and // `FOO` is dropped last. }); scope.spawn(|_| { thread::sleep(ms(100)); s.send(()).unwrap(); }); }) .unwrap(); }