tests for when a thread-local gets initialized in a tls dtor
This commit is contained in:
parent
aded2be375
commit
f071a205c7
6 changed files with 97 additions and 64 deletions
|
|
@ -4,27 +4,28 @@
|
|||
use std::cell::RefCell;
|
||||
use std::thread;
|
||||
|
||||
struct TestCell {
|
||||
value: RefCell<u8>,
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
struct TestCell {
|
||||
value: RefCell<u8>,
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
// 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) } };
|
||||
}
|
||||
|
||||
// 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>>>,
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static B: JoinCell = JoinCell { value: RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// 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();
|
||||
}
|
||||
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::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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 TestCell {
|
||||
impl Drop for Bar {
|
||||
fn drop(&mut self) {
|
||||
eprintln!("Dropping: {}", *self.value.borrow())
|
||||
println!("Bar dtor");
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static A: TestCell = TestCell { value: RefCell::new(0) };
|
||||
static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } };
|
||||
struct Foo;
|
||||
|
||||
impl Drop for Foo {
|
||||
fn drop(&mut self) {
|
||||
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 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(|_| {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
Continue main.
|
||||
Dropping: 5
|
||||
Dropping: 5
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Foo dtor
|
||||
Bar dtor
|
||||
Loading…
Add table
Add a link
Reference in a new issue