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:: cell:: OnceCell ;
9
+ use std:: sync:: { Mutex , MutexGuard , PoisonError , TryLockError } ;
9
10
10
11
/// Ergonomic global variables.
11
12
///
@@ -25,7 +26,8 @@ use std::sync::{Mutex, MutexGuard, TryLockError};
25
26
/// For access, you should primarily use [`lock()`](Self::lock). There is also [`try_lock()`](Self::try_lock) for special cases.
26
27
pub struct Global < 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 < OnceCell < T > > ,
30
+ init_fn : fn ( ) -> T ,
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 ( OnceCell :: new ( ) ) ,
41
+ init_fn,
39
42
}
40
43
}
41
44
@@ -57,72 +60,39 @@ 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" ) ;
63
+ let guard = self . value . lock ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
64
+ guard. get_or_init ( self . init_fn ) ;
64
65
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
66
+ // SAFETY: above line guarantees init
67
+ unsafe { GlobalGuard :: new_unchecked ( guard) }
69
68
}
70
69
71
- /// Non-panicking access with error introspection.
70
+ /// Non-blocking access with error introspection.
72
71
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
- } ;
72
+ /// Initializes the cell and returns a guard.
73
+ fn init < ' mutex : ' cell , ' cell , T > (
74
+ g : MutexGuard < ' mutex , OnceCell < T > > ,
75
+ init_fn : fn ( ) -> T ,
76
+ ) -> Result < GlobalGuard < ' cell , T > , GlobalLockError < ' cell , T > > {
77
+ // initialize the cell
78
+ std:: panic:: catch_unwind ( || g. get_or_init ( init_fn) )
79
+ . map_err ( |_| GlobalLockError :: InitFailed ) ?;
80
+
81
+ // SAFETY: `get_or_init()` cant panic, therefore the object is guaranteed to be initialized.
82
+ Ok ( unsafe { GlobalGuard :: new_unchecked ( g) } )
83
+ }
88
84
89
- guard. ok_or ( GlobalLockError :: InitFailed )
90
- }
85
+ match self . value . try_lock ( ) {
86
+ Ok ( guard) => init ( guard, self . init_fn ) ,
87
+ Err ( TryLockError :: WouldBlock ) => Err ( GlobalLockError :: WouldBlock ) ,
91
88
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) } ) ;
89
+ // this is a cold branch, where the initialization function panicked.
90
+ Err ( TryLockError :: Poisoned ( x) ) => {
91
+ // we do the same things as in the hot branch
92
+ let circumvent = init ( x. into_inner ( ) , self . init_fn ) ?;
93
+ Err ( GlobalLockError :: Poisoned { circumvent } )
100
94
}
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 ;
120
- }
121
- }
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) } )
95
+ }
126
96
}
127
97
}
128
98
@@ -131,57 +101,49 @@ impl<T> Global<T> {
131
101
132
102
// Encapsulate private fields.
133
103
mod global_guard {
104
+ use super :: * ;
134
105
use std:: ops:: { Deref , DerefMut } ;
135
- use std:: sync:: MutexGuard ;
136
-
137
- use super :: InitState ;
138
106
139
107
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
140
108
pub struct GlobalGuard < ' a , T > {
141
- // Safety invariant: Is `Initialized`.
142
- mutex_guard : MutexGuard < ' a , InitState < T > > ,
109
+ mutex_guard : MutexGuard < ' a , OnceCell < T > > ,
143
110
}
144
111
145
112
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 } ) ,
113
+ pub ( super ) fn new ( mutex_guard : MutexGuard < ' a , OnceCell < T > > ) -> Option < Self > {
114
+ match mutex_guard. get ( ) {
115
+ Some ( _) => Some ( Self { mutex_guard } ) ,
149
116
_ => None ,
150
117
}
151
118
}
152
119
153
120
/// # Safety
154
121
///
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
-
122
+ /// The value must be initialized.
123
+ pub ( super ) unsafe fn new_unchecked ( mutex_guard : MutexGuard < ' a , OnceCell < T > > ) -> Self {
124
+ debug_assert ! ( mutex_guard. get( ) . is_some( ) , "safety precondition violated" ) ;
159
125
Self :: new ( mutex_guard) . unwrap_unchecked ( )
160
126
}
161
127
}
162
128
163
129
impl < T > Deref for GlobalGuard < ' _ , T > {
164
130
type Target = T ;
165
-
166
131
fn deref ( & self ) -> & Self :: Target {
167
- // SAFETY: `self` is `Initialized` .
168
- unsafe { self . mutex_guard . as_initialized ( ) . unwrap_unchecked ( ) }
132
+ // SAFETY: [`GlobalGuard::new`] being the sole constructor guarantees that the cell is initialized .
133
+ unsafe { self . mutex_guard . get ( ) . unwrap_unchecked ( ) }
169
134
}
170
135
}
171
136
172
137
impl < T > DerefMut for GlobalGuard < ' _ , T > {
173
138
fn deref_mut ( & mut self ) -> & mut Self :: Target {
174
- // SAFETY: `self` is `Initialized` .
175
- unsafe { self . mutex_guard . as_initialized_mut ( ) . unwrap_unchecked ( ) }
139
+ // SAFETY: [`GlobalGuard::new`] being the sole constructor guarantees that the cell is initialized .
140
+ unsafe { self . mutex_guard . get_mut ( ) . unwrap_unchecked ( ) }
176
141
}
177
142
}
178
143
}
179
144
180
145
pub use global_guard:: GlobalGuard ;
181
146
182
- // ----------------------------------------------------------------------------------------------------------------------------------------------
183
- // Errors
184
-
185
147
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
186
148
pub enum GlobalLockError < ' a , T > {
187
149
/// The mutex is currently locked by another thread.
@@ -194,31 +156,6 @@ pub enum GlobalLockError<'a, T> {
194
156
InitFailed ,
195
157
}
196
158
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
159
// ----------------------------------------------------------------------------------------------------------------------------------------------
223
160
// Tests
224
161
0 commit comments