Skip to content

Commit 4f8dc8f

Browse files
committed
Auto merge of #3708 - RalfJung:tls-dtor-in-dtor, r=RalfJung
tests for when a thread-local gets initialized in a tls dtor
2 parents b8e1d7e + f071a20 commit 4f8dc8f

File tree

6 files changed

+91
-58
lines changed

6 files changed

+91
-58
lines changed

src/tools/miri/tests/pass/tls/tls_macro_drop.rs

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,28 @@
44
use std::cell::RefCell;
55
use std::thread;
66

7-
struct TestCell {
8-
value: RefCell<u8>,
9-
}
7+
/// Check that destructors of the library thread locals are executed immediately
8+
/// after a thread terminates.
9+
fn check_destructors() {
10+
struct TestCell {
11+
value: RefCell<u8>,
12+
}
1013

11-
impl Drop for TestCell {
12-
fn drop(&mut self) {
13-
for _ in 0..10 {
14-
thread::yield_now();
14+
impl Drop for TestCell {
15+
fn drop(&mut self) {
16+
for _ in 0..10 {
17+
thread::yield_now();
18+
}
19+
println!("Dropping: {} (should be before 'Continue main 1').", *self.value.borrow())
1520
}
16-
println!("Dropping: {} (should be before 'Continue main 1').", *self.value.borrow())
1721
}
18-
}
1922

20-
thread_local! {
21-
static A: TestCell = TestCell { value: RefCell::new(0) };
22-
static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } };
23-
}
23+
// Test both regular and `const` thread-locals.
24+
thread_local! {
25+
static A: TestCell = TestCell { value: RefCell::new(0) };
26+
static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } };
27+
}
2428

25-
/// Check that destructors of the library thread locals are executed immediately
26-
/// after a thread terminates.
27-
fn check_destructors() {
2829
// We use the same value for both of them, since destructor order differs between Miri on Linux
2930
// (which uses `register_dtor_fallback`, in the end using a single pthread_key to manage a
3031
// thread-local linked list of dtors to call), real Linux rustc (which uses
@@ -44,26 +45,29 @@ fn check_destructors() {
4445
println!("Continue main 1.")
4546
}
4647

47-
struct JoinCell {
48-
value: RefCell<Option<thread::JoinHandle<u8>>>,
49-
}
48+
/// Check that the destructor can be blocked joining another thread.
49+
fn check_blocking() {
50+
struct JoinCell {
51+
value: RefCell<Option<thread::JoinHandle<u8>>>,
52+
}
5053

51-
impl Drop for JoinCell {
52-
fn drop(&mut self) {
53-
for _ in 0..10 {
54-
thread::yield_now();
54+
impl Drop for JoinCell {
55+
fn drop(&mut self) {
56+
for _ in 0..10 {
57+
thread::yield_now();
58+
}
59+
let join_handle = self.value.borrow_mut().take().unwrap();
60+
println!(
61+
"Joining: {} (should be before 'Continue main 2').",
62+
join_handle.join().unwrap()
63+
);
5564
}
56-
let join_handle = self.value.borrow_mut().take().unwrap();
57-
println!("Joining: {} (should be before 'Continue main 2').", join_handle.join().unwrap());
5865
}
59-
}
6066

61-
thread_local! {
62-
static B: JoinCell = JoinCell { value: RefCell::new(None) };
63-
}
67+
thread_local! {
68+
static B: JoinCell = JoinCell { value: RefCell::new(None) };
69+
}
6470

65-
/// Check that the destructor can be blocked joining another thread.
66-
fn check_blocking() {
6771
thread::spawn(|| {
6872
B.with(|f| {
6973
assert!(f.value.borrow().is_none());
@@ -74,10 +78,36 @@ fn check_blocking() {
7478
.join()
7579
.unwrap();
7680
println!("Continue main 2.");
77-
// Preempt the main thread so that the destructor gets executed and can join
78-
// the thread.
79-
thread::yield_now();
80-
thread::yield_now();
81+
}
82+
83+
fn check_tls_init_in_dtor() {
84+
struct Bar;
85+
86+
impl Drop for Bar {
87+
fn drop(&mut self) {
88+
println!("Bar dtor (should be before `Continue main 3`).");
89+
}
90+
}
91+
92+
struct Foo;
93+
94+
impl Drop for Foo {
95+
fn drop(&mut self) {
96+
println!("Foo dtor (should be before `Bar dtor`).");
97+
// We initialize another thread-local inside the dtor, which is an interesting corner case.
98+
thread_local!(static BAR: Bar = Bar);
99+
BAR.with(|_| {});
100+
}
101+
}
102+
103+
thread_local!(static FOO: Foo = Foo);
104+
105+
thread::spawn(|| {
106+
FOO.with(|_| {});
107+
})
108+
.join()
109+
.unwrap();
110+
println!("Continue main 3.");
81111
}
82112

83113
// This test tests that TLS destructors have run before the thread joins. The
@@ -248,6 +278,8 @@ fn dtors_in_dtors_in_dtors() {
248278
fn main() {
249279
check_destructors();
250280
check_blocking();
281+
check_tls_init_in_dtor();
282+
251283
join_orders_after_tls_destructors();
252284
dtors_in_dtors_in_dtors();
253285
}

src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ Dropping: 8 (should be before 'Continue main 1').
33
Continue main 1.
44
Joining: 7 (should be before 'Continue main 2').
55
Continue main 2.
6+
Foo dtor (should be before `Bar dtor`).
7+
Bar dtor (should be before `Continue main 3`).
8+
Continue main 3.

src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ Dropping: 8 (should be before 'Continue main 1').
33
Continue main 1.
44
Joining: 7 (should be before 'Continue main 2').
55
Continue main 2.
6+
Foo dtor (should be before `Bar dtor`).
7+
Bar dtor (should be before `Continue main 3`).
8+
Continue main 3.
Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,27 @@
1-
//! Check that destructors of the thread locals are executed on all OSes
2-
//! (even when we do not support concurrency, and cannot run the other test).
1+
//! Check that destructors of main thread thread locals are executed.
32
4-
use std::cell::RefCell;
3+
struct Bar;
54

6-
struct TestCell {
7-
value: RefCell<u8>,
5+
impl Drop for Bar {
6+
fn drop(&mut self) {
7+
println!("Bar dtor");
8+
}
89
}
910

10-
impl Drop for TestCell {
11+
struct Foo;
12+
13+
impl Drop for Foo {
1114
fn drop(&mut self) {
12-
eprintln!("Dropping: {}", *self.value.borrow())
15+
println!("Foo dtor");
16+
// We initialize another thread-local inside the dtor, which is an interesting corner case.
17+
// Also we use a `const` thread-local here, just to also have that code path covered.
18+
thread_local!(static BAR: Bar = const { Bar });
19+
BAR.with(|_| {});
1320
}
1421
}
1522

16-
thread_local! {
17-
static A: TestCell = TestCell { value: RefCell::new(0) };
18-
static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } };
19-
}
23+
thread_local!(static FOO: Foo = Foo);
2024

2125
fn main() {
22-
A.with(|f| {
23-
assert_eq!(*f.value.borrow(), 0);
24-
*f.value.borrow_mut() = 5;
25-
});
26-
A_CONST.with(|f| {
27-
assert_eq!(*f.value.borrow(), 10);
28-
*f.value.borrow_mut() = 5; // Same value as above since the drop order is different on different platforms
29-
});
30-
eprintln!("Continue main.")
26+
FOO.with(|_| {});
3127
}

src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Foo dtor
2+
Bar dtor

0 commit comments

Comments
 (0)