diff options
| author | bors <bors@rust-lang.org> | 2024-06-24 06:51:00 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-06-24 06:51:00 +0000 |
| commit | 4f8dc8fa3e5e50ab024252397d272f773f3a2a8d (patch) | |
| tree | 8614f2c4ae3be1930c65e1013515fe4e3f64e18f | |
| parent | b8e1d7ef6fcec31db99ef82d8a913a2a703e8944 (diff) | |
| parent | f071a205c7f3946835c834e252786f02046211f7 (diff) | |
| download | rust-4f8dc8fa3e5e50ab024252397d272f773f3a2a8d.tar.gz rust-4f8dc8fa3e5e50ab024252397d272f773f3a2a8d.zip | |
Auto merge of #3708 - RalfJung:tls-dtor-in-dtor, r=RalfJung
tests for when a thread-local gets initialized in a tls dtor
6 files changed, 91 insertions, 58 deletions
diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop.rs b/src/tools/miri/tests/pass/tls/tls_macro_drop.rs index bd06eec9cd5..0d8a1cef511 100644 --- a/src/tools/miri/tests/pass/tls/tls_macro_drop.rs +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop.rs @@ -4,27 +4,28 @@ use std::cell::RefCell; use std::thread; -struct TestCell { - value: RefCell<u8>, -} +/// Check that destructors of the library thread locals are executed immediately +/// after a thread terminates. +fn check_destructors() { + struct TestCell { + value: RefCell<u8>, + } -impl Drop for TestCell { - fn drop(&mut self) { - for _ in 0..10 { - thread::yield_now(); + impl Drop for TestCell { + fn drop(&mut self) { + for _ in 0..10 { + thread::yield_now(); + } + println!("Dropping: {} (should be before 'Continue main 1').", *self.value.borrow()) } - println!("Dropping: {} (should be before 'Continue main 1').", *self.value.borrow()) } -} -thread_local! { - static A: TestCell = TestCell { value: RefCell::new(0) }; - static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } }; -} + // Test both regular and `const` thread-locals. + thread_local! { + static A: TestCell = TestCell { value: RefCell::new(0) }; + static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } }; + } -/// Check that destructors of the library thread locals are executed immediately -/// after a thread terminates. -fn check_destructors() { // We use the same value for both of them, since destructor order differs between Miri on Linux // (which uses `register_dtor_fallback`, in the end using a single pthread_key to manage a // thread-local linked list of dtors to call), real Linux rustc (which uses @@ -44,26 +45,29 @@ fn check_destructors() { println!("Continue main 1.") } -struct JoinCell { - value: RefCell<Option<thread::JoinHandle<u8>>>, -} +/// Check that the destructor can be blocked joining another thread. +fn check_blocking() { + struct JoinCell { + value: RefCell<Option<thread::JoinHandle<u8>>>, + } -impl Drop for JoinCell { - fn drop(&mut self) { - for _ in 0..10 { - thread::yield_now(); + impl Drop for JoinCell { + fn drop(&mut self) { + for _ in 0..10 { + thread::yield_now(); + } + let join_handle = self.value.borrow_mut().take().unwrap(); + println!( + "Joining: {} (should be before 'Continue main 2').", + join_handle.join().unwrap() + ); } - let join_handle = self.value.borrow_mut().take().unwrap(); - println!("Joining: {} (should be before 'Continue main 2').", join_handle.join().unwrap()); } -} -thread_local! { - static B: JoinCell = JoinCell { value: RefCell::new(None) }; -} + thread_local! { + static B: JoinCell = JoinCell { value: RefCell::new(None) }; + } -/// Check that the destructor can be blocked joining another thread. -fn check_blocking() { thread::spawn(|| { B.with(|f| { assert!(f.value.borrow().is_none()); @@ -74,10 +78,36 @@ fn check_blocking() { .join() .unwrap(); println!("Continue main 2."); - // Preempt the main thread so that the destructor gets executed and can join - // the thread. - thread::yield_now(); - thread::yield_now(); +} + +fn check_tls_init_in_dtor() { + struct Bar; + + impl Drop for Bar { + fn drop(&mut self) { + println!("Bar dtor (should be before `Continue main 3`)."); + } + } + + struct Foo; + + impl Drop for Foo { + fn drop(&mut self) { + println!("Foo dtor (should be before `Bar dtor`)."); + // We initialize another thread-local inside the dtor, which is an interesting corner case. + thread_local!(static BAR: Bar = Bar); + BAR.with(|_| {}); + } + } + + thread_local!(static FOO: Foo = Foo); + + thread::spawn(|| { + FOO.with(|_| {}); + }) + .join() + .unwrap(); + println!("Continue main 3."); } // This test tests that TLS destructors have run before the thread joins. The @@ -248,6 +278,8 @@ fn dtors_in_dtors_in_dtors() { fn main() { check_destructors(); check_blocking(); + check_tls_init_in_dtor(); + join_orders_after_tls_destructors(); dtors_in_dtors_in_dtors(); } diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout b/src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout index b7877820a0c..3e17acc8328 100644 --- a/src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout @@ -3,3 +3,6 @@ Dropping: 8 (should be before 'Continue main 1'). Continue main 1. Joining: 7 (should be before 'Continue main 2'). Continue main 2. +Foo dtor (should be before `Bar dtor`). +Bar dtor (should be before `Continue main 3`). +Continue main 3. diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout b/src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout index b7877820a0c..3e17acc8328 100644 --- a/src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout @@ -3,3 +3,6 @@ Dropping: 8 (should be before 'Continue main 1'). Continue main 1. Joining: 7 (should be before 'Continue main 2'). Continue main 2. +Foo dtor (should be before `Bar dtor`). +Bar dtor (should be before `Continue main 3`). +Continue main 3. diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs index f36c460ae53..082a6f17838 100644 --- a/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs @@ -1,31 +1,27 @@ -//! Check that destructors of the thread locals are executed on all OSes -//! (even when we do not support concurrency, and cannot run the other test). +//! Check that destructors of main thread thread locals are executed. -use std::cell::RefCell; +struct Bar; -struct TestCell { - value: RefCell<u8>, +impl Drop for Bar { + fn drop(&mut self) { + println!("Bar dtor"); + } } -impl Drop for TestCell { +struct Foo; + +impl Drop for Foo { fn drop(&mut self) { - eprintln!("Dropping: {}", *self.value.borrow()) + println!("Foo dtor"); + // We initialize another thread-local inside the dtor, which is an interesting corner case. + // Also we use a `const` thread-local here, just to also have that code path covered. + thread_local!(static BAR: Bar = const { Bar }); + BAR.with(|_| {}); } } -thread_local! { - static A: TestCell = TestCell { value: RefCell::new(0) }; - static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } }; -} +thread_local!(static FOO: Foo = Foo); fn main() { - A.with(|f| { - assert_eq!(*f.value.borrow(), 0); - *f.value.borrow_mut() = 5; - }); - A_CONST.with(|f| { - assert_eq!(*f.value.borrow(), 10); - *f.value.borrow_mut() = 5; // Same value as above since the drop order is different on different platforms - }); - eprintln!("Continue main.") + FOO.with(|_| {}); } diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr deleted file mode 100644 index 09ec1c3c2c5..00000000000 --- a/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr +++ /dev/null @@ -1,3 +0,0 @@ -Continue main. -Dropping: 5 -Dropping: 5 diff --git a/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stdout b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stdout new file mode 100644 index 00000000000..6160f272649 --- /dev/null +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stdout @@ -0,0 +1,2 @@ +Foo dtor +Bar dtor |
