]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs
Rollup merge of #106878 - JohnTitor:issue-92157, r=compiler-errors
[rust.git] / src / tools / miri / tests / pass-dep / concurrency / tls_pthread_drop_order.rs
1 //@ignore-target-windows: No libc on Windows
2 //! Test that pthread_key destructors are run in the right order.
3 //! Note that these are *not* used by actual `thread_local!` on Linux! Those use
4 //! `thread_local_dtor::register_dtor` from the stdlib instead. In Miri this hits the fallback path
5 //! in `register_dtor_fallback`, which uses a *single* pthread_key to manage a thread-local list of
6 //! dtors to call.
7
8 use std::mem;
9 use std::ptr;
10
11 pub type Key = libc::pthread_key_t;
12
13 static mut RECORD: usize = 0;
14 static mut KEYS: [Key; 2] = [0; 2];
15 static mut GLOBALS: [u64; 2] = [1, 0];
16
17 static mut CANNARY: *mut u64 = ptr::null_mut(); // this serves as a cannary: if TLS dtors are not run properly, this will not get deallocated, making the test fail.
18
19 pub unsafe fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
20     let mut key = 0;
21     assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
22     key
23 }
24
25 pub unsafe fn set(key: Key, value: *mut u8) {
26     let r = libc::pthread_setspecific(key, value as *mut _);
27     assert_eq!(r, 0);
28 }
29
30 pub fn record(r: usize) {
31     assert!(r < 10);
32     unsafe { RECORD = RECORD * 10 + r };
33 }
34
35 unsafe extern "C" fn dtor(ptr: *mut u64) {
36     assert!(CANNARY != ptr::null_mut()); // make sure we do not get run too often
37     let val = *ptr;
38
39     let which_key =
40         GLOBALS.iter().position(|global| global as *const _ == ptr).expect("Should find my global");
41     record(which_key);
42
43     if val > 0 {
44         *ptr = val - 1;
45         set(KEYS[which_key], ptr as *mut _);
46     }
47
48     // Check if the records matches what we expect. If yes, clear the cannary.
49     // If the record is wrong, the cannary will never get cleared, leading to a leak -> test fails.
50     // If the record is incomplete (i.e., more dtor calls happen), the check at the beginning of this function will fail -> test fails.
51     // The correct sequence is: First key 0, then key 1, then key 0.
52     // Note that this relies on dtor order, which is not specified by POSIX, but seems to be
53     // consistent between Miri and Linux currently (as of Aug 2022).
54     if RECORD == 0_1_0 {
55         drop(Box::from_raw(CANNARY));
56         CANNARY = ptr::null_mut();
57     }
58 }
59
60 fn main() {
61     unsafe {
62         create(None); // check that the no-dtor case works
63
64         // Initialize the keys we use to check destructor ordering
65         for (key, global) in KEYS.iter_mut().zip(GLOBALS.iter_mut()) {
66             *key = create(Some(mem::transmute(dtor as unsafe extern "C" fn(*mut u64))));
67             set(*key, global as *mut _ as *mut u8);
68         }
69
70         // Initialize cannary
71         CANNARY = Box::into_raw(Box::new(0u64));
72     }
73 }