5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- use std:: sync:: { Mutex , MutexGuard , TryLockError } ;
8
+ use std:: sync:: { Mutex , OnceLock , PoisonError , TryLockError } ;
9
9
10
10
/// Ergonomic global variables.
11
11
///
@@ -23,9 +23,10 @@ use std::sync::{Mutex, MutexGuard, TryLockError};
23
23
///
24
24
/// There are two `const` methods for construction: [`new()`](Self::new) and [`default()`](Self::default).
25
25
/// For access, you should primarily use [`lock()`](Self::lock). There is also [`try_lock()`](Self::try_lock) for special cases.
26
- pub struct Global < T > {
26
+ pub struct Global < T , F = fn ( ) -> T > {
27
27
// When needed, this could be changed to use RwLock and separate read/write guards.
28
- value : Mutex < InitState < T > > ,
28
+ value : Mutex < OnceLock < T > > ,
29
+ init_fn : F ,
29
30
}
30
31
31
32
impl < T > Global < T > {
@@ -35,7 +36,8 @@ impl<T> Global<T> {
35
36
pub const fn new ( init_fn : fn ( ) -> T ) -> Self {
36
37
// Note: could be generalized to F: FnOnce() -> T + Send. See also once_cell::Lazy<T, F>.
37
38
Self {
38
- value : Mutex :: new ( InitState :: Pending ( init_fn) ) ,
39
+ value : Mutex :: new ( OnceLock :: new ( ) ) ,
40
+ init_fn,
39
41
}
40
42
}
41
43
@@ -57,72 +59,30 @@ impl<T> Global<T> {
57
59
/// If the initialization function panics. Once that happens, the global is considered poisoned and all future calls to `lock()` will panic.
58
60
/// This can currently not be recovered from.
59
61
pub fn lock ( & self ) -> GlobalGuard < ' _ , T > {
60
- let mutex_guard = self
61
- . value
62
- . lock ( )
63
- . expect ( "Global<T> poisoned; a thread has panicked while holding a lock to it" ) ;
64
-
65
- let guard = Self :: ensure_init ( mutex_guard, true )
66
- . unwrap_or_else ( || panic ! ( "previous Global<T> initialization failed due to panic" ) ) ;
67
-
68
- guard
62
+ let guard = self . value . lock ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
63
+ guard. get_or_init ( self . init_fn ) ;
64
+ // SAFETY: above line guarantees init
65
+ unsafe { GlobalGuard :: new_unchecked ( guard) }
69
66
}
70
67
71
- /// Non-panicking access with error introspection.
68
+ /// Non-blocking access with error introspection.
72
69
pub fn try_lock ( & self ) -> Result < GlobalGuard < ' _ , T > , GlobalLockError < ' _ , T > > {
73
- let guard = match self . value . try_lock ( ) {
74
- Ok ( mutex_guard) => Self :: ensure_init ( mutex_guard, false ) ,
75
- Err ( TryLockError :: WouldBlock ) => {
76
- return Err ( GlobalLockError :: WouldBlock ) ;
77
- }
78
- Err ( TryLockError :: Poisoned ( poisoned) ) => {
79
- return Err ( GlobalLockError :: Poisoned {
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
- } ) ;
86
- }
87
- } ;
88
-
89
- guard. ok_or ( GlobalLockError :: InitFailed )
90
- }
91
-
92
- fn ensure_init (
93
- mut mutex_guard : MutexGuard < ' _ , InitState < T > > ,
94
- may_panic : bool ,
95
- ) -> Option < GlobalGuard < ' _ , T > > {
96
- let init_fn = match & mut * mutex_guard {
97
- InitState :: Initialized ( _) => {
98
- // SAFETY: `mutex_guard` is `Initialized`.
99
- return Some ( unsafe { GlobalGuard :: new_unchecked ( mutex_guard) } ) ;
100
- }
101
- InitState :: Failed => {
102
- return None ;
103
- }
104
- InitState :: Pending ( init_fn) => init_fn,
105
- } ;
106
-
107
- // Unwinding should be safe here, as there is no unsafe code relying on it.
108
- let init_fn = std:: panic:: AssertUnwindSafe ( init_fn) ;
109
- match std:: panic:: catch_unwind ( init_fn) {
110
- Ok ( value) => * mutex_guard = InitState :: Initialized ( value) ,
111
- Err ( e) => {
112
- eprintln ! ( "panic during Global<T> initialization" ) ;
113
- * mutex_guard = InitState :: Failed ;
114
-
115
- if may_panic {
116
- std:: panic:: resume_unwind ( e) ;
117
- } else {
118
- // Note: this currently swallows panic.
119
- return None ;
70
+ let guard = self . value . try_lock ( ) . map_err ( |x| match x {
71
+ TryLockError :: Poisoned ( x) => {
72
+ let g = x. into_inner ( ) ;
73
+ match std:: panic:: catch_unwind ( || g. get_or_init ( self . init_fn ) ) {
74
+ Ok ( _) => GlobalLockError :: Poisoned {
75
+ // SAFETY: see get_or_init.
76
+ circumvent : unsafe { GlobalGuard :: new_unchecked ( g) } ,
77
+ } ,
78
+ Err ( _) => GlobalLockError :: InitFailed ,
120
79
}
121
80
}
122
- } ;
123
-
124
- // SAFETY: `mutex_guard` was either set to `Initialized` above, or we returned from the function.
125
- Some ( unsafe { GlobalGuard :: new_unchecked ( mutex_guard) } )
81
+ TryLockError :: WouldBlock => GlobalLockError :: WouldBlock ,
82
+ } ) ?;
83
+ std:: panic:: catch_unwind ( || guard. get_or_init ( self . init_fn ) )
84
+ . map_err ( |_| GlobalLockError :: InitFailed ) ?;
85
+ Ok ( unsafe { GlobalGuard :: new_unchecked ( guard) } )
126
86
}
127
87
}
128
88
@@ -131,57 +91,50 @@ impl<T> Global<T> {
131
91
132
92
// Encapsulate private fields.
133
93
mod global_guard {
134
- use std:: ops :: { Deref , DerefMut } ;
135
- use std :: sync :: MutexGuard ;
136
-
137
- use super :: InitState ;
94
+ use std:: {
95
+ ops :: { Deref , DerefMut } ,
96
+ sync :: { MutexGuard , OnceLock } ,
97
+ } ;
138
98
139
99
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
140
100
pub struct GlobalGuard < ' a , T > {
141
- // Safety invariant: Is `Initialized`.
142
- mutex_guard : MutexGuard < ' a , InitState < T > > ,
101
+ mutex_guard : MutexGuard < ' a , OnceLock < T > > ,
143
102
}
144
103
145
104
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 } ) ,
105
+ pub ( super ) fn new ( mutex_guard : MutexGuard < ' a , OnceLock < T > > ) -> Option < Self > {
106
+ match mutex_guard. get ( ) {
107
+ Some ( _) => Some ( Self { mutex_guard } ) ,
149
108
_ => None ,
150
109
}
151
110
}
152
111
153
112
/// # Safety
154
113
///
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
-
114
+ /// The value must be initialized.
115
+ pub ( super ) unsafe fn new_unchecked ( mutex_guard : MutexGuard < ' a , OnceLock < T > > ) -> Self {
116
+ debug_assert ! ( mutex_guard. get( ) . is_some( ) ) ;
159
117
Self :: new ( mutex_guard) . unwrap_unchecked ( )
160
118
}
161
119
}
162
120
163
- impl < T > Deref for GlobalGuard < ' _ , T > {
121
+ impl < ' a , T > Deref for GlobalGuard < ' a , T > {
164
122
type Target = T ;
165
-
166
123
fn deref ( & self ) -> & Self :: Target {
167
- // SAFETY: `self` is `Initialized`.
168
- unsafe { self . mutex_guard . as_initialized ( ) . unwrap_unchecked ( ) }
124
+ // SAFETY: must have been init (see GlobalGuard::new)
125
+ unsafe { self . mutex_guard . get ( ) . unwrap_unchecked ( ) }
169
126
}
170
127
}
171
128
172
129
impl < T > DerefMut for GlobalGuard < ' _ , T > {
173
130
fn deref_mut ( & mut self ) -> & mut Self :: Target {
174
- // SAFETY: `self` is `Initialized`.
175
- unsafe { self . mutex_guard . as_initialized_mut ( ) . unwrap_unchecked ( ) }
131
+ unsafe { self . mutex_guard . get_mut ( ) . unwrap_unchecked ( ) }
176
132
}
177
133
}
178
134
}
179
135
180
136
pub use global_guard:: GlobalGuard ;
181
137
182
- // ----------------------------------------------------------------------------------------------------------------------------------------------
183
- // Errors
184
-
185
138
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
186
139
pub enum GlobalLockError < ' a , T > {
187
140
/// The mutex is currently locked by another thread.
@@ -194,31 +147,6 @@ pub enum GlobalLockError<'a, T> {
194
147
InitFailed ,
195
148
}
196
149
197
- // ----------------------------------------------------------------------------------------------------------------------------------------------
198
- // Internals
199
-
200
- enum InitState < T > {
201
- Initialized ( T ) ,
202
- Pending ( fn ( ) -> T ) ,
203
- Failed ,
204
- }
205
-
206
- impl < T > InitState < T > {
207
- fn as_initialized ( & self ) -> Option < & T > {
208
- match self {
209
- InitState :: Initialized ( t) => Some ( t) ,
210
- _ => None ,
211
- }
212
- }
213
-
214
- fn as_initialized_mut ( & mut self ) -> Option < & mut T > {
215
- match self {
216
- InitState :: Initialized ( t) => Some ( t) ,
217
- _ => None ,
218
- }
219
- }
220
- }
221
-
222
150
// ----------------------------------------------------------------------------------------------------------------------------------------------
223
151
// Tests
224
152
@@ -283,6 +211,7 @@ mod tests {
283
211
284
212
#[ test]
285
213
fn test_global_poison ( ) {
214
+ std:: panic:: set_hook ( Box :: new ( |_| ( ) ) ) ;
286
215
let result = std:: panic:: catch_unwind ( || {
287
216
let guard = POISON . lock ( ) ;
288
217
panic ! ( "poison injection" ) ;
0 commit comments