about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-24 06:51:00 +0000
committerbors <bors@rust-lang.org>2024-06-24 06:51:00 +0000
commit4f8dc8fa3e5e50ab024252397d272f773f3a2a8d (patch)
tree8614f2c4ae3be1930c65e1013515fe4e3f64e18f
parentb8e1d7ef6fcec31db99ef82d8a913a2a703e8944 (diff)
parentf071a205c7f3946835c834e252786f02046211f7 (diff)
downloadrust-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
-rw-r--r--src/tools/miri/tests/pass/tls/tls_macro_drop.rs102
-rw-r--r--src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout3
-rw-r--r--src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout3
-rw-r--r--src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs36
-rw-r--r--src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr3
-rw-r--r--src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stdout2
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