5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- use std:: ops:: { Deref , DerefMut } ;
9
8
use std:: sync:: { Mutex , MutexGuard , TryLockError } ;
10
9
11
10
/// Ergonomic global variables.
@@ -66,7 +65,6 @@ impl<T> Global<T> {
66
65
let guard = Self :: ensure_init ( mutex_guard, true )
67
66
. unwrap_or_else ( || panic ! ( "previous Global<T> initialization failed due to panic" ) ) ;
68
67
69
- debug_assert ! ( matches!( * guard. mutex_guard, InitState :: Initialized ( _) ) ) ;
70
68
guard
71
69
}
72
70
@@ -79,45 +77,31 @@ impl<T> Global<T> {
79
77
}
80
78
Err ( TryLockError :: Poisoned ( poisoned) ) => {
81
79
return Err ( GlobalLockError :: Poisoned {
82
- circumvent : GlobalGuard {
83
- mutex_guard : poisoned. into_inner ( ) ,
84
- } ,
80
+ // We can likely use `new_unchecked` here, but verifying that it's safe would need somewhat tricky reasoning.
81
+ // Since this error condition isn't very common, it is likely not very important to optimize access to the value here.
82
+ // Especially since most users will likely not want to access it anyway.
83
+ circumvent : GlobalGuard :: new ( poisoned. into_inner ( ) )
84
+ . expect ( "Poisoned global guard should always be initialized" ) ,
85
85
} ) ;
86
86
}
87
87
} ;
88
88
89
- match guard {
90
- None => Err ( GlobalLockError :: InitFailed ) ,
91
- Some ( guard) => {
92
- debug_assert ! ( matches!( * guard. mutex_guard, InitState :: Initialized ( _) ) ) ;
93
- Ok ( guard)
94
- }
95
- }
89
+ guard. ok_or ( GlobalLockError :: InitFailed )
96
90
}
97
91
98
92
fn ensure_init (
99
93
mut mutex_guard : MutexGuard < ' _ , InitState < T > > ,
100
94
may_panic : bool ,
101
95
) -> Option < GlobalGuard < ' _ , T > > {
102
- let pending_state = match & mut * mutex_guard {
96
+ let init_fn = match & mut * mutex_guard {
103
97
InitState :: Initialized ( _) => {
104
- return Some ( GlobalGuard { mutex_guard } ) ;
105
- }
106
- InitState :: TransientInitializing => {
107
- // SAFETY: only set inside this function and all paths (panic + return) leave the enum in a different state.
108
- unsafe { std:: hint:: unreachable_unchecked ( ) } ;
98
+ // SAFETY: `mutex_guard` is `Initialized`.
99
+ return Some ( unsafe { GlobalGuard :: new_unchecked ( mutex_guard) } ) ;
109
100
}
110
101
InitState :: Failed => {
111
102
return None ;
112
103
}
113
- state @ InitState :: Pending ( _) => {
114
- std:: mem:: replace ( state, InitState :: TransientInitializing )
115
- }
116
- } ;
117
-
118
- let InitState :: Pending ( init_fn) = pending_state else {
119
- // SAFETY: all other paths leave the function, see above.
120
- unsafe { std:: hint:: unreachable_unchecked ( ) }
104
+ InitState :: Pending ( init_fn) => init_fn,
121
105
} ;
122
106
123
107
// Unwinding should be safe here, as there is no unsafe code relying on it.
@@ -137,32 +121,64 @@ impl<T> Global<T> {
137
121
}
138
122
} ;
139
123
140
- Some ( GlobalGuard { mutex_guard } )
124
+ // SAFETY: `mutex_guard` was either set to `Initialized` above, or we returned from the function.
125
+ Some ( unsafe { GlobalGuard :: new_unchecked ( mutex_guard) } )
141
126
}
142
127
}
143
128
144
129
// ----------------------------------------------------------------------------------------------------------------------------------------------
145
130
// Guards
146
131
147
- /// Guard that temporarily gives access to a `Global<T>`'s inner value .
148
- pub struct GlobalGuard < ' a , T > {
149
- mutex_guard : MutexGuard < ' a , InitState < T > > ,
150
- }
132
+ // Encapsulate private fields .
133
+ mod global_guard {
134
+ use std :: ops :: { Deref , DerefMut } ;
135
+ use std :: sync :: MutexGuard ;
151
136
152
- impl < T > Deref for GlobalGuard < ' _ , T > {
153
- type Target = T ;
137
+ use super :: InitState ;
154
138
155
- fn deref ( & self ) -> & Self :: Target {
156
- self . mutex_guard . unwrap_ref ( )
139
+ /// Guard that temporarily gives access to a `Global<T>`'s inner value.
140
+ pub struct GlobalGuard < ' a , T > {
141
+ // Safety invariant: Is `Initialized`.
142
+ mutex_guard : MutexGuard < ' a , InitState < T > > ,
157
143
}
158
- }
159
144
160
- impl < T > DerefMut for GlobalGuard < ' _ , T > {
161
- fn deref_mut ( & mut self ) -> & mut Self :: Target {
162
- self . mutex_guard . unwrap_mut ( )
145
+ impl < ' a , T > GlobalGuard < ' a , T > {
146
+ pub ( super ) fn new ( mutex_guard : MutexGuard < ' a , InitState < T > > ) -> Option < Self > {
147
+ match & * mutex_guard {
148
+ InitState :: Initialized ( _) => Some ( Self { mutex_guard } ) ,
149
+ _ => None ,
150
+ }
151
+ }
152
+
153
+ /// # Safety
154
+ ///
155
+ /// The value must be `Initialized`.
156
+ pub ( super ) unsafe fn new_unchecked ( mutex_guard : MutexGuard < ' a , InitState < T > > ) -> Self {
157
+ debug_assert ! ( matches!( * mutex_guard, InitState :: Initialized ( _) ) ) ;
158
+
159
+ Self :: new ( mutex_guard) . unwrap_unchecked ( )
160
+ }
161
+ }
162
+
163
+ impl < T > Deref for GlobalGuard < ' _ , T > {
164
+ type Target = T ;
165
+
166
+ fn deref ( & self ) -> & Self :: Target {
167
+ // SAFETY: `self` is `Initialized`.
168
+ unsafe { self . mutex_guard . as_initialized ( ) . unwrap_unchecked ( ) }
169
+ }
170
+ }
171
+
172
+ impl < T > DerefMut for GlobalGuard < ' _ , T > {
173
+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
174
+ // SAFETY: `self` is `Initialized`.
175
+ unsafe { self . mutex_guard . as_initialized_mut ( ) . unwrap_unchecked ( ) }
176
+ }
163
177
}
164
178
}
165
179
180
+ pub use global_guard:: GlobalGuard ;
181
+
166
182
// ----------------------------------------------------------------------------------------------------------------------------------------------
167
183
// Errors
168
184
@@ -184,28 +200,21 @@ pub enum GlobalLockError<'a, T> {
184
200
enum InitState < T > {
185
201
Initialized ( T ) ,
186
202
Pending ( fn ( ) -> T ) ,
187
- TransientInitializing ,
188
203
Failed ,
189
204
}
190
205
191
206
impl < T > InitState < T > {
192
- fn unwrap_ref ( & self ) -> & T {
207
+ fn as_initialized ( & self ) -> Option < & T > {
193
208
match self {
194
- InitState :: Initialized ( t) => t,
195
- _ => {
196
- // SAFETY: This method is only called from a guard, which can only be obtained in Initialized state.
197
- unsafe { std:: hint:: unreachable_unchecked ( ) }
198
- }
209
+ InitState :: Initialized ( t) => Some ( t) ,
210
+ _ => None ,
199
211
}
200
212
}
201
213
202
- fn unwrap_mut ( & mut self ) -> & mut T {
214
+ fn as_initialized_mut ( & mut self ) -> Option < & mut T > {
203
215
match self {
204
- InitState :: Initialized ( t) => t,
205
- _ => {
206
- // SAFETY: This method is only called from a guard, which can only be obtained in Initialized state.
207
- unsafe { std:: hint:: unreachable_unchecked ( ) }
208
- }
216
+ InitState :: Initialized ( t) => Some ( t) ,
217
+ _ => None ,
209
218
}
210
219
}
211
220
}
0 commit comments