2022-07-17 20:54:10 -05:00
//@ignore-target-windows: No libc on Windows
2022-08-22 11:13:42 -05:00
//! Test that pthread_key destructors are run in the right order.
//! Note that these are *not* used by actual `thread_local!` on Linux! Those use
//! `thread_local_dtor::register_dtor` from the stdlib instead. In Miri this hits the fallback path
//! in `register_dtor_fallback`, which uses a *single* pthread_key to manage a thread-local list of
//! dtors to call.
2017-06-21 21:24:21 -05:00
use std ::mem ;
2022-06-26 20:14:18 -05:00
use std ::ptr ;
2017-06-21 21:24:21 -05:00
pub type Key = libc ::pthread_key_t ;
2022-06-20 17:30:34 -05:00
static mut RECORD : usize = 0 ;
static mut KEYS : [ Key ; 2 ] = [ 0 ; 2 ] ;
static mut GLOBALS : [ u64 ; 2 ] = [ 1 , 0 ] ;
2017-06-21 21:24:21 -05:00
2022-06-26 20:14:18 -05:00
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.
2017-06-21 21:24:21 -05:00
2022-06-20 17:30:34 -05:00
pub unsafe fn create ( dtor : Option < unsafe extern " C " fn ( * mut u8 ) > ) -> Key {
2017-06-21 21:24:21 -05:00
let mut key = 0 ;
assert_eq! ( libc ::pthread_key_create ( & mut key , mem ::transmute ( dtor ) ) , 0 ) ;
key
}
pub unsafe fn set ( key : Key , value : * mut u8 ) {
let r = libc ::pthread_setspecific ( key , value as * mut _ ) ;
assert_eq! ( r , 0 ) ;
}
pub fn record ( r : usize ) {
assert! ( r < 10 ) ;
2022-06-20 17:30:34 -05:00
unsafe { RECORD = RECORD * 10 + r } ;
2017-06-21 21:24:21 -05:00
}
2022-06-20 17:30:34 -05:00
unsafe extern " C " fn dtor ( ptr : * mut u64 ) {
2022-06-26 20:14:18 -05:00
assert! ( CANNARY ! = ptr ::null_mut ( ) ) ; // make sure we do not get run too often
2017-06-21 21:24:21 -05:00
let val = * ptr ;
2017-08-12 11:45:44 -05:00
2022-06-20 17:30:34 -05:00
let which_key =
GLOBALS . iter ( ) . position ( | global | global as * const _ = = ptr ) . expect ( " Should find my global " ) ;
2017-06-21 21:24:21 -05:00
record ( which_key ) ;
if val > 0 {
2022-06-20 17:30:34 -05:00
* ptr = val - 1 ;
2017-06-21 21:24:21 -05:00
set ( KEYS [ which_key ] , ptr as * mut _ ) ;
}
// Check if the records matches what we expect. If yes, clear the cannary.
2017-06-21 23:54:42 -05:00
// If the record is wrong, the cannary will never get cleared, leading to a leak -> test fails.
2017-06-21 21:24:21 -05:00
// If the record is incomplete (i.e., more dtor calls happen), the check at the beginning of this function will fail -> test fails.
// The correct sequence is: First key 0, then key 1, then key 0.
2022-08-22 11:13:42 -05:00
// Note that this relies on dtor order, which is not specified by POSIX, but seems to be
// consistent between Miri and Linux currently (as of Aug 2022).
2017-06-21 21:24:21 -05:00
if RECORD = = 0_1_0 {
drop ( Box ::from_raw ( CANNARY ) ) ;
2022-06-26 20:14:18 -05:00
CANNARY = ptr ::null_mut ( ) ;
2017-06-21 21:24:21 -05:00
}
}
fn main ( ) {
unsafe {
create ( None ) ; // check that the no-dtor case works
// Initialize the keys we use to check destructor ordering
2019-05-28 11:28:15 -05:00
for ( key , global ) in KEYS . iter_mut ( ) . zip ( GLOBALS . iter_mut ( ) ) {
2022-06-20 17:30:34 -05:00
* key = create ( Some ( mem ::transmute ( dtor as unsafe extern " C " fn ( * mut u64 ) ) ) ) ;
2020-10-31 10:22:16 -05:00
set ( * key , global as * mut _ as * mut u8 ) ;
2017-06-21 21:24:21 -05:00
}
// Initialize cannary
CANNARY = Box ::into_raw ( Box ::new ( 0 u64 ) ) ;
}
}