1 //! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics. 2 3 #![cfg(not(miri))] // Miri detects that this test is buggy: the destructor of `FOO` uses `std::thread::current()`! 4 5 use std::thread; 6 use std::time::Duration; 7 8 use crossbeam_channel::{select, unbounded}; 9 use crossbeam_utils::thread::scope; 10 ms(ms: u64) -> Duration11fn ms(ms: u64) -> Duration { 12 Duration::from_millis(ms) 13 } 14 15 #[test] 16 #[cfg_attr(target_os = "macos", ignore = "TLS is destroyed too early on macOS")] use_while_exiting()17fn use_while_exiting() { 18 struct Foo; 19 20 impl Drop for Foo { 21 fn drop(&mut self) { 22 // A blocking operation after the thread-locals have been dropped. This will attempt to 23 // use the thread-locals and must not panic. 24 let (_s, r) = unbounded::<()>(); 25 select! { 26 recv(r) -> _ => {} 27 default(ms(100)) => {} 28 } 29 } 30 } 31 32 thread_local! { 33 static FOO: Foo = const { Foo }; 34 } 35 36 let (s, r) = unbounded::<()>(); 37 38 scope(|scope| { 39 scope.spawn(|_| { 40 // First initialize `FOO`, then the thread-locals related to crossbeam-channel. 41 FOO.with(|_| ()); 42 r.recv().unwrap(); 43 // At thread exit, thread-locals related to crossbeam-channel get dropped first and 44 // `FOO` is dropped last. 45 }); 46 47 scope.spawn(|_| { 48 thread::sleep(ms(100)); 49 s.send(()).unwrap(); 50 }); 51 }) 52 .unwrap(); 53 } 54