1
- use crate :: cell:: UnsafeCell ;
2
- use crate :: hint :: unreachable_unchecked ;
1
+ use crate :: cell:: { Cell , UnsafeCell } ;
2
+ use crate :: mem :: MaybeUninit ;
3
3
use crate :: ptr;
4
4
use crate :: sys:: thread_local:: { abort_on_dtor_unwind, destructors} ;
5
5
6
- pub unsafe trait DestroyedState : Sized {
6
+ pub unsafe trait DestroyedState : Sized + Copy {
7
7
fn register_dtor < T > ( s : & Storage < T , Self > ) ;
8
8
}
9
9
@@ -19,23 +19,29 @@ unsafe impl DestroyedState for () {
19
19
}
20
20
}
21
21
22
- enum State < T , D > {
23
- Initial ,
24
- Alive ( T ) ,
22
+ #[ derive( Copy , Clone ) ]
23
+ enum State < D > {
24
+ Uninitialized ,
25
+ Initializing ,
26
+ Alive ,
25
27
Destroyed ( D ) ,
26
28
}
27
29
28
30
#[ allow( missing_debug_implementations) ]
29
31
pub struct Storage < T , D > {
30
- state : UnsafeCell < State < T , D > > ,
32
+ state : Cell < State < D > > ,
33
+ value : UnsafeCell < MaybeUninit < T > > ,
31
34
}
32
35
33
36
impl < T , D > Storage < T , D >
34
37
where
35
38
D : DestroyedState ,
36
39
{
37
40
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
+ }
39
45
}
40
46
41
47
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -49,35 +55,45 @@ where
49
55
/// The `self` reference must remain valid until the TLS destructor is run.
50
56
#[ inline]
51
57
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)
57
62
}
58
63
}
59
64
60
65
#[ 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
+ }
65
74
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
+ }
74
80
}
75
81
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.
77
90
unsafe {
78
- let State :: Alive ( v) = & * self . state . get ( ) else { unreachable_unchecked ( ) } ;
79
- v
91
+ self . value . get ( ) . write ( MaybeUninit :: new ( v) ) ;
80
92
}
93
+
94
+ self . state . set ( State :: Alive ) ;
95
+ D :: register_dtor ( self ) ;
96
+ self . value . get ( ) . cast ( )
81
97
}
82
98
}
83
99
@@ -92,9 +108,13 @@ unsafe extern "C" fn destroy<T>(ptr: *mut u8) {
92
108
// Print a nice abort message if a panic occurs.
93
109
abort_on_dtor_unwind ( || {
94
110
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
+ }
99
119
} )
100
120
}
0 commit comments