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,28 @@ 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
+ Alive ,
25
26
Destroyed ( D ) ,
26
27
}
27
28
28
29
#[ allow( missing_debug_implementations) ]
29
30
pub struct Storage < T , D > {
30
- state : UnsafeCell < State < T , D > > ,
31
+ state : Cell < State < D > > ,
32
+ value : UnsafeCell < MaybeUninit < T > > ,
31
33
}
32
34
33
35
impl < T , D > Storage < T , D >
34
36
where
35
37
D : DestroyedState ,
36
38
{
37
39
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
+ }
39
44
}
40
45
41
46
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -49,35 +54,49 @@ where
49
54
/// The `self` reference must remain valid until the TLS destructor is run.
50
55
#[ inline]
51
56
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) }
57
61
}
58
62
}
59
63
64
+ /// # Safety
65
+ /// The `self` reference must remain valid until the TLS destructor is run.
60
66
#[ 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
+ }
63
77
64
78
let v = i. and_then ( Option :: take) . unwrap_or_else ( f) ;
65
79
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 ) {
68
87
// If the variable is not being recursively initialized, register
69
88
// the destructor. This might be a noop if the value does not need
70
89
// 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 ) ,
75
91
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 ! ( ) ,
80
97
}
98
+
99
+ self . value . get ( ) . cast ( )
81
100
}
82
101
}
83
102
@@ -92,9 +111,12 @@ unsafe extern "C" fn destroy<T>(ptr: *mut u8) {
92
111
// Print a nice abort message if a panic occurs.
93
112
abort_on_dtor_unwind ( || {
94
113
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
+ }
99
121
} )
100
122
}
0 commit comments