1010//! [1]: https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989
1111
1212use core:: ffi:: c_void;
13+ use core:: sync:: atomic:: { AtomicU32 , Ordering } ;
1314
1415use crate :: cell:: Cell ;
1516use crate :: ptr;
16- use crate :: sys:: c;
17+ use crate :: sys:: c:: { self , FLS_OUT_OF_INDEXES } ;
1718
1819pub type Key = u32 ;
1920
@@ -35,15 +36,33 @@ unsafe fn set(key: Key, ptr: *const c_void) {
3536 }
3637}
3738
39+ static KEY : AtomicU32 = AtomicU32 :: new ( FLS_OUT_OF_INDEXES ) ;
40+
3841pub fn enable ( ) {
3942 #[ thread_local]
4043 static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
4144
4245 if !REGISTERED . replace ( true ) {
43- unsafe {
44- let key = create ( Some ( cleanup) ) ;
45- set ( key, ptr:: dangling ( ) ) ;
46+ let current_key = KEY . load ( Ordering :: Acquire ) ;
47+
48+ // If we already allocated a key, we only need to set it to a non-null value so that the dtor is run.
49+ let key = if current_key != FLS_OUT_OF_INDEXES {
50+ current_key
51+ } else {
52+ // Otherwise, we try to allocate a key.
53+ let new_key = unsafe { create ( Some ( cleanup) ) } ;
54+
55+ // Now we need to set this key to be used by everyone else.
56+ // If we won the race, our key is the right one and we can set it to non-null value.
57+ // If we lost, we'll use the winning key.
58+ // Note: we are not freeing our losing key since according to the docs
59+ // > It is expected that DLLs call [the FlsFree] function (if at all) only during DLL_PROCESS_DETACH.
60+ match KEY . compare_exchange ( current_key, new_key, Ordering :: Release , Ordering :: Acquire ) {
61+ Ok ( _) => new_key,
62+ Err ( other_key) => other_key,
63+ }
4664 } ;
65+ unsafe { set ( key, ptr:: without_provenance ( 1 ) ) } ;
4766 }
4867}
4968
0 commit comments