1- use crate :: cell:: UnsafeCell ;
2- use crate :: hint :: unreachable_unchecked ;
1+ use crate :: cell:: { Cell , UnsafeCell } ;
2+ use crate :: mem :: MaybeUninit ;
33use crate :: ptr;
44use crate :: sys:: thread_local:: { abort_on_dtor_unwind, destructors} ;
55
6- pub unsafe trait DestroyedState : Sized {
6+ pub unsafe trait DestroyedState : Sized + Copy {
77 fn register_dtor < T > ( s : & Storage < T , Self > ) ;
88}
99
@@ -19,23 +19,29 @@ unsafe impl DestroyedState for () {
1919 }
2020}
2121
22- enum State < T , D > {
23- Initial ,
24- Alive ( T ) ,
22+ #[ derive( Copy , Clone ) ]
23+ enum State < D > {
24+ Uninitialized ,
25+ Initializing ,
26+ Alive ,
2527 Destroyed ( D ) ,
2628}
2729
2830#[ allow( missing_debug_implementations) ]
2931pub struct Storage < T , D > {
30- state : UnsafeCell < State < T , D > > ,
32+ state : Cell < State < D > > ,
33+ value : UnsafeCell < MaybeUninit < T > > ,
3134}
3235
3336impl < T , D > Storage < T , D >
3437where
3538 D : DestroyedState ,
3639{
3740 pub const fn new ( ) -> Storage < T , D > {
38- Storage { state : UnsafeCell :: new ( State :: Initial ) }
41+ Storage {
42+ state : Cell :: new ( State :: Uninitialized ) ,
43+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
44+ }
3945 }
4046
4147 /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -49,35 +55,45 @@ where
4955 /// The `self` reference must remain valid until the TLS destructor is run.
5056 #[ inline]
5157 pub unsafe fn get_or_init ( & self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
52- let state = unsafe { & * self . state . get ( ) } ;
53- match state {
54- State :: Alive ( v) => v,
55- State :: Destroyed ( _) => ptr:: null ( ) ,
56- State :: Initial => unsafe { self . initialize ( i, f) } ,
58+ if let State :: Alive = self . state . get ( ) {
59+ self . value . get ( ) . cast ( )
60+ } else {
61+ self . get_or_init_slow ( i, f)
5762 }
5863 }
5964
6065 #[ cold]
61- unsafe fn initialize ( & self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
62- // Perform initialization
63-
64- let v = i. and_then ( Option :: take) . unwrap_or_else ( f) ;
66+ fn get_or_init_slow ( & self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
67+ // Ensure we have unique access to an uninitialized value.
68+ match self . state . get ( ) {
69+ State :: Uninitialized => self . state . set ( State :: Initializing ) ,
70+ State :: Initializing => panic ! ( "thread_local initializer recursively depends on itself" ) ,
71+ State :: Alive => return self . value . get ( ) . cast ( ) ,
72+ State :: Destroyed ( _) => return ptr:: null ( ) ,
73+ }
6574
66- let old = unsafe { self . state . get ( ) . replace ( State :: Alive ( v) ) } ;
67- match old {
68- // If the variable is not being recursively initialized, register
69- // the destructor. This might be a noop if the value does not need
70- // destruction.
71- State :: Initial => D :: register_dtor ( self ) ,
72- // Else, drop the old value. This might be changed to a panic.
73- val => drop ( val) ,
75+ struct BackToUninitOnPanic < ' a , D > ( & ' a Cell < State < D > > ) ;
76+ impl < ' a , D > Drop for BackToUninitOnPanic < ' a , D > {
77+ fn drop ( & mut self ) {
78+ self . 0 . set ( State :: Uninitialized ) ;
79+ }
7480 }
7581
76- // SAFETY: the state was just set to `Alive`
82+ // Get the initial value, making sure that we restore the state to uninitialized
83+ // should f panic.
84+ let on_panic = BackToUninitOnPanic ( & self . state ) ;
85+ let v = i. and_then ( Option :: take) . unwrap_or_else ( f) ;
86+ crate :: mem:: forget ( on_panic) ;
87+
88+ // SAFETY: we are !Sync so we have exclusive access to self.value. We also ensured
89+ // that the state was uninitialized so we aren't replacing a value we must keep alive.
7790 unsafe {
78- let State :: Alive ( v) = & * self . state . get ( ) else { unreachable_unchecked ( ) } ;
79- v
91+ self . value . get ( ) . write ( MaybeUninit :: new ( v) ) ;
8092 }
93+
94+ self . state . set ( State :: Alive ) ;
95+ D :: register_dtor ( self ) ;
96+ self . value . get ( ) . cast ( )
8197 }
8298}
8399
@@ -92,9 +108,13 @@ unsafe extern "C" fn destroy<T>(ptr: *mut u8) {
92108 // Print a nice abort message if a panic occurs.
93109 abort_on_dtor_unwind ( || {
94110 let storage = unsafe { & * ( ptr as * const Storage < T , ( ) > ) } ;
95- // Update the state before running the destructor as it may attempt to
96- // access the variable.
97- let val = unsafe { storage. state . get ( ) . replace ( State :: Destroyed ( ( ) ) ) } ;
98- drop ( val) ;
111+ if let State :: Alive = storage. state . replace ( State :: Destroyed ( ( ) ) ) {
112+ // SAFETY: we ensured the state was Alive, and prevented running the destructor
113+ // twice by updating the state to Destroyed. This is necessary as the destructor
114+ // may attempt to access the variable.
115+ unsafe {
116+ crate :: ptr:: drop_in_place ( storage. value . get ( ) . cast :: < T > ( ) ) ;
117+ }
118+ }
99119 } )
100120}
0 commit comments