11#![ cfg( target_thread_local) ]
22#![ unstable( feature = "thread_local_internals" , issue = "0" ) ]
33
4- use crate :: cell:: { Cell , UnsafeCell } ;
5- use crate :: mem;
6- use crate :: ptr;
7-
8-
9- pub struct Key < T > {
10- inner : UnsafeCell < Option < T > > ,
11-
12- // Metadata to keep track of the state of the destructor. Remember that
13- // these variables are thread-local, not global.
14- dtor_registered : Cell < bool > ,
15- dtor_running : Cell < bool > ,
16- }
17-
18- unsafe impl < T > Sync for Key < T > { }
19-
20- impl < T > Key < T > {
21- pub const fn new ( ) -> Key < T > {
22- Key {
23- inner : UnsafeCell :: new ( None ) ,
24- dtor_registered : Cell :: new ( false ) ,
25- dtor_running : Cell :: new ( false )
26- }
27- }
28-
29- pub fn get ( & ' static self ) -> Option < & ' static UnsafeCell < Option < T > > > {
30- unsafe {
31- if mem:: needs_drop :: < T > ( ) && self . dtor_running . get ( ) {
32- return None
33- }
34- self . register_dtor ( ) ;
35- }
36- Some ( & self . inner )
37- }
38-
39- unsafe fn register_dtor ( & self ) {
40- if !mem:: needs_drop :: < T > ( ) || self . dtor_registered . get ( ) {
41- return
42- }
43-
44- register_dtor ( self as * const _ as * mut u8 ,
45- destroy_value :: < T > ) ;
46- self . dtor_registered . set ( true ) ;
47- }
48- }
49-
50- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern fn ( * mut u8 ) ) {
51- // The fallback implementation uses a vanilla OS-based TLS key to track
52- // the list of destructors that need to be run for this thread. The key
53- // then has its own destructor which runs all the other destructors.
54- //
55- // The destructor for DTORS is a little special in that it has a `while`
56- // loop to continuously drain the list of registered destructors. It
57- // *should* be the case that this loop always terminates because we
58- // provide the guarantee that a TLS key cannot be set after it is
59- // flagged for destruction.
60- use crate :: sys_common:: thread_local as os;
61-
62- static DTORS : os:: StaticKey = os:: StaticKey :: new ( Some ( run_dtors) ) ;
63- type List = Vec < ( * mut u8 , unsafe extern fn ( * mut u8 ) ) > ;
64- if DTORS . get ( ) . is_null ( ) {
65- let v: Box < List > = box Vec :: new ( ) ;
66- DTORS . set ( Box :: into_raw ( v) as * mut u8 ) ;
67- }
68- let list: & mut List = & mut * ( DTORS . get ( ) as * mut List ) ;
69- list. push ( ( t, dtor) ) ;
70-
71- unsafe extern fn run_dtors ( mut ptr : * mut u8 ) {
72- while !ptr. is_null ( ) {
73- let list: Box < List > = Box :: from_raw ( ptr as * mut List ) ;
74- for ( ptr, dtor) in list. into_iter ( ) {
75- dtor ( ptr) ;
76- }
77- ptr = DTORS . get ( ) ;
78- DTORS . set ( ptr:: null_mut ( ) ) ;
79- }
80- }
81- }
82-
83- pub unsafe extern fn destroy_value < T > ( ptr : * mut u8 ) {
84- let ptr = ptr as * mut Key < T > ;
85- // Right before we run the user destructor be sure to flag the
86- // destructor as running for this thread so calls to `get` will return
87- // `None`.
88- ( * ptr) . dtor_running . set ( true ) ;
89-
90- // The macOS implementation of TLS apparently had an odd aspect to it
91- // where the pointer we have may be overwritten while this destructor
92- // is running. Specifically if a TLS destructor re-accesses TLS it may
93- // trigger a re-initialization of all TLS variables, paving over at
94- // least some destroyed ones with initial values.
95- //
96- // This means that if we drop a TLS value in place on macOS that we could
97- // revert the value to its original state halfway through the
98- // destructor, which would be bad!
99- //
100- // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
101- // instead of drop_in_place.
102- if cfg ! ( target_os = "macos" ) {
103- ptr:: read ( ( * ptr) . inner . get ( ) ) ;
104- } else {
105- ptr:: drop_in_place ( ( * ptr) . inner . get ( ) ) ;
106- }
107- }
4+ pub use crate :: sys_common:: thread_local:: register_dtor_fallback as register_dtor;
0 commit comments