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:: ops:: { Deref , DerefMut } ;
9
+ use std:: sync:: { Mutex , MutexGuard , OnceLock , PoisonError , TryLockError } ;
9
10
10
11
/// Ergonomic global variables.
11
12
///
@@ -23,9 +24,10 @@ use std::sync::{Mutex, MutexGuard, TryLockError};
23
24
///
24
25
/// There are two `const` methods for construction: [`new()`](Self::new) and [`default()`](Self::default).
25
26
/// 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 > {
27
+ pub struct Global < T , F = fn ( ) -> T > {
27
28
// When needed, this could be changed to use RwLock and separate read/write guards.
28
- value : Mutex < InitState < T > > ,
29
+ value : Mutex < OnceLock < T > > ,
30
+ init_fn : F ,
29
31
}
30
32
31
33
impl < T > Global < T > {
@@ -35,7 +37,8 @@ impl<T> Global<T> {
35
37
pub const fn new ( init_fn : fn ( ) -> T ) -> Self {
36
38
// Note: could be generalized to F: FnOnce() -> T + Send. See also once_cell::Lazy<T, F>.
37
39
Self {
38
- value : Mutex :: new ( InitState :: Pending ( init_fn) ) ,
40
+ value : Mutex :: new ( OnceLock :: new ( ) ) ,
41
+ init_fn,
39
42
}
40
43
}
41
44
@@ -57,131 +60,53 @@ impl<T> Global<T> {
57
60
/// If the initialization function panics. Once that happens, the global is considered poisoned and all future calls to `lock()` will panic.
58
61
/// This can currently not be recovered from.
59
62
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
63
+ let guard = self . value . lock ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
64
+ guard. get_or_init ( self . init_fn ) ;
65
+ GlobalGuard { mutex_guard : 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 mutex_guard = x. into_inner ( ) ;
73
+ match std:: panic:: catch_unwind ( || mutex_guard. get_or_init ( self . init_fn ) ) {
74
+ Ok ( _) => GlobalLockError :: Poisoned {
75
+ circumvent : GlobalGuard { mutex_guard } ,
76
+ } ,
77
+ Err ( _) => GlobalLockError :: InitFailed ,
120
78
}
121
79
}
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) } )
80
+ TryLockError :: WouldBlock => GlobalLockError :: WouldBlock ,
81
+ } ) ?;
82
+ std:: panic:: catch_unwind ( || guard. get_or_init ( self . init_fn ) )
83
+ . map_err ( |_| GlobalLockError :: InitFailed ) ?;
84
+ Ok ( GlobalGuard { mutex_guard : guard } )
126
85
}
127
86
}
128
87
129
88
// ----------------------------------------------------------------------------------------------------------------------------------------------
130
89
// Guards
131
90
132
- // Encapsulate private fields .
133
- mod global_guard {
134
- use std :: ops :: { Deref , DerefMut } ;
135
- use std :: sync :: MutexGuard ;
91
+ /// Guard that temporarily gives access to a `Global<T>`'s inner value .
92
+ pub struct GlobalGuard < ' a , T > {
93
+ mutex_guard : MutexGuard < ' a , OnceLock < T > > ,
94
+ }
136
95
137
96
use super :: InitState ;
138
97
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 > > ,
143
- }
144
-
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
- }
98
+ fn deref ( & self ) -> & Self :: Target {
99
+ // SAFETY: must have been init
100
+ unsafe { self . mutex_guard . get ( ) . unwrap_unchecked ( ) }
170
101
}
102
+ }
171
103
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
- }
104
+ impl < T > DerefMut for GlobalGuard < ' _ , T > {
105
+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
106
+ unsafe { self . mutex_guard . get_mut ( ) . unwrap_unchecked ( ) }
177
107
}
178
108
}
179
109
180
- pub use global_guard:: GlobalGuard ;
181
-
182
- // ----------------------------------------------------------------------------------------------------------------------------------------------
183
- // Errors
184
-
185
110
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
186
111
pub enum GlobalLockError < ' a , T > {
187
112
/// The mutex is currently locked by another thread.
@@ -194,31 +119,6 @@ pub enum GlobalLockError<'a, T> {
194
119
InitFailed ,
195
120
}
196
121
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
122
// ----------------------------------------------------------------------------------------------------------------------------------------------
223
123
// Tests
224
124
@@ -283,6 +183,7 @@ mod tests {
283
183
284
184
#[ test]
285
185
fn test_global_poison ( ) {
186
+ std:: panic:: set_hook ( Box :: new ( |_| ( ) ) ) ;
286
187
let result = std:: panic:: catch_unwind ( || {
287
188
let guard = POISON . lock ( ) ;
288
189
panic ! ( "poison injection" ) ;
0 commit comments