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,28 @@ 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+ Alive ,
2526 Destroyed ( D ) ,
2627}
2728
2829#[ allow( missing_debug_implementations) ]
2930pub struct Storage < T , D > {
30- state : UnsafeCell < State < T , D > > ,
31+ state : Cell < State < D > > ,
32+ value : UnsafeCell < MaybeUninit < T > > ,
3133}
3234
3335impl < T , D > Storage < T , D >
3436where
3537 D : DestroyedState ,
3638{
3739 pub const fn new ( ) -> Storage < T , D > {
38- Storage { state : UnsafeCell :: new ( State :: Initial ) }
40+ Storage {
41+ state : Cell :: new ( State :: Uninitialized ) ,
42+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
43+ }
3944 }
4045
4146 /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -49,35 +54,49 @@ where
4954 /// The `self` reference must remain valid until the TLS destructor is run.
5055 #[ inline]
5156 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) } ,
57+ if let State :: Alive = self . state . get ( ) {
58+ self . value . get ( ) . cast ( )
59+ } else {
60+ unsafe { self . get_or_init_slow ( i, f) }
5761 }
5862 }
5963
64+ /// # Safety
65+ /// The `self` reference must remain valid until the TLS destructor is run.
6066 #[ cold]
61- unsafe fn initialize ( & self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
62- // Perform initialization
67+ unsafe fn get_or_init_slow (
68+ & self ,
69+ i : Option < & mut Option < T > > ,
70+ f : impl FnOnce ( ) -> T ,
71+ ) -> * const T {
72+ match self . state . get ( ) {
73+ State :: Uninitialized => { }
74+ State :: Alive => return self . value . get ( ) . cast ( ) ,
75+ State :: Destroyed ( _) => return ptr:: null ( ) ,
76+ }
6377
6478 let v = i. and_then ( Option :: take) . unwrap_or_else ( f) ;
6579
66- let old = unsafe { self . state . get ( ) . replace ( State :: Alive ( v) ) } ;
67- match old {
80+ // SAFETY: we cannot be inside a `LocalKey::with` scope, as the initializer
81+ // has already returned and the next scope only starts after we return
82+ // the pointer. Therefore, there can be no references to the old value,
83+ // even if it was initialized. Thus because we are !Sync we have exclusive
84+ // access to self.value and may replace it.
85+ let mut old_value = unsafe { self . value . get ( ) . replace ( MaybeUninit :: new ( v) ) } ;
86+ match self . state . replace ( State :: Alive ) {
6887 // If the variable is not being recursively initialized, register
6988 // the destructor. This might be a noop if the value does not need
7089 // 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) ,
74- }
90+ State :: Uninitialized => D :: register_dtor ( self ) ,
7591
76- // SAFETY: the state was just set to `Alive`
77- unsafe {
78- let State :: Alive ( v) = & * self . state . get ( ) else { unreachable_unchecked ( ) } ;
79- v
92+ // Recursive initialization, we only need to drop the old value
93+ // as we've already registered the destructor.
94+ State :: Alive => unsafe { old_value. assume_init_drop ( ) } ,
95+
96+ State :: Destroyed ( _) => unreachable ! ( ) ,
8097 }
98+
99+ self . value . get ( ) . cast ( )
81100 }
82101}
83102
@@ -92,9 +111,12 @@ unsafe extern "C" fn destroy<T>(ptr: *mut u8) {
92111 // Print a nice abort message if a panic occurs.
93112 abort_on_dtor_unwind ( || {
94113 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) ;
114+ if let State :: Alive = storage. state . replace ( State :: Destroyed ( ( ) ) ) {
115+ // SAFETY: we ensured the state was Alive so the value was initialized.
116+ // We also updated the state to Destroyed to prevent the destructor
117+ // from accessing the thread-local variable, as this would violate
118+ // the exclusive access provided by &mut T in Drop::drop.
119+ unsafe { ( * storage. value . get ( ) ) . assume_init_drop ( ) }
120+ }
99121 } )
100122}
0 commit comments