@@ -4,7 +4,7 @@ use std::fmt;
4
4
use std:: mem:: MaybeUninit ;
5
5
use std:: ops:: Drop ;
6
6
use std:: ptr;
7
- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
7
+ use std:: sync:: atomic:: { AtomicU8 , Ordering } ;
8
8
9
9
// This file contains an implementation of an SetOnce. The value of SetOnce
10
10
// can only be modified once during initialization.
@@ -70,11 +70,22 @@ use std::sync::atomic::{AtomicBool, Ordering};
70
70
/// }
71
71
/// ```
72
72
pub struct SetOnce < T > {
73
- value_set : AtomicBool ,
73
+ // AtomicU8 has 3 states, EMPTY, SETTING and DONE
74
+ // is described by the `AtomicState` enum.
75
+ value_set : AtomicU8 ,
74
76
value : UnsafeCell < MaybeUninit < T > > ,
75
77
notify : Notify ,
76
78
}
77
79
80
+ #[ repr( u8 ) ]
81
+ #[ derive( PartialEq , Eq ) ]
82
+ pub ( crate ) enum AtomicState {
83
+ Empty = 0 ,
84
+ // SETTING means "set is currently running"
85
+ Setting = 1 ,
86
+ Done = 2 ,
87
+ }
88
+
78
89
impl < T > Default for SetOnce < T > {
79
90
fn default ( ) -> SetOnce < T > {
80
91
SetOnce :: new ( )
@@ -106,22 +117,22 @@ impl<T: Eq> Eq for SetOnce<T> {}
106
117
impl < T > Drop for SetOnce < T > {
107
118
fn drop ( & mut self ) {
108
119
if self . initialized ( ) {
109
- // SAFETY: We're inside the drop implementation of SetOnce
110
- // AND we're also initalized. This is the best way to ensure
111
- // out data gets dropped
120
+ // SAFETY: Inside the drop implementation of SetOnce also
121
+ // initalized. we cast `MaybeUninit<T>` to *mut T
122
+ // because `MaybeUninit<T>` doesnt have a destructor
112
123
unsafe {
113
- let _ = self . value . with_mut ( |ptr| ptr:: read ( ptr) . assume_init ( ) ) ;
124
+ self . value . with_mut ( |ptr| ptr:: drop_in_place ( ptr as * mut T ) ) ;
114
125
}
115
- // no need to set the flag to false as this set once is being
116
- // dropped
126
+ // no need to change value_set here as SetOnce is being dropped
117
127
}
118
128
}
119
129
}
120
130
121
131
impl < T > From < T > for SetOnce < T > {
122
132
fn from ( value : T ) -> Self {
123
133
SetOnce {
124
- value_set : AtomicBool :: new ( true ) ,
134
+ // value has been initialized
135
+ value_set : AtomicU8 :: new ( AtomicState :: Done as _ ) ,
125
136
value : UnsafeCell :: new ( MaybeUninit :: new ( value) ) ,
126
137
notify : Notify :: new ( ) ,
127
138
}
@@ -132,7 +143,7 @@ impl<T> SetOnce<T> {
132
143
/// Creates a new empty `SetOnce` instance.
133
144
pub fn new ( ) -> Self {
134
145
Self {
135
- value_set : AtomicBool :: new ( false ) ,
146
+ value_set : AtomicU8 :: new ( AtomicState :: Empty as _ ) ,
136
147
value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
137
148
notify : Notify :: new ( ) ,
138
149
}
@@ -174,7 +185,7 @@ impl<T> SetOnce<T> {
174
185
#[ cfg( not( all( loom, test) ) ) ]
175
186
pub const fn const_new ( ) -> Self {
176
187
Self {
177
- value_set : AtomicBool :: new ( false ) ,
188
+ value_set : AtomicU8 :: new ( AtomicState :: Empty as _ ) ,
178
189
value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
179
190
notify : Notify :: const_new ( ) ,
180
191
}
@@ -224,7 +235,7 @@ impl<T> SetOnce<T> {
224
235
#[ cfg( not( all( loom, test) ) ) ]
225
236
pub const fn const_new_with ( value : T ) -> Self {
226
237
Self {
227
- value_set : AtomicBool :: new ( true ) ,
238
+ value_set : AtomicU8 :: new ( AtomicState :: Done as _ ) ,
228
239
value : UnsafeCell :: new ( MaybeUninit :: new ( value) ) ,
229
240
notify : Notify :: const_new ( ) ,
230
241
}
@@ -235,7 +246,7 @@ impl<T> SetOnce<T> {
235
246
pub fn initialized ( & self ) -> bool {
236
247
// Using acquire ordering so we're able to read/catch any writes that
237
248
// are done with `Ordering::Release`
238
- self . value_set . load ( Ordering :: Acquire )
249
+ self . value_set . load ( Ordering :: Acquire ) == AtomicState :: Done as _
239
250
}
240
251
241
252
// SAFETY: The SetOnce must not be empty.
@@ -254,13 +265,19 @@ impl<T> SetOnce<T> {
254
265
}
255
266
256
267
// SAFETY: The caller of this function needs to ensure that this function is
257
- // called only when the value_set AtomicBool is flipped from FALSE to TRUE
258
- // meaning that the value is being set from uinitialized to initialized via
259
- // this function
268
+ // called only when the self.value_set AtomicUsize is flipped from
269
+ // AtomicState::Empty to AtomicState::Setting meaning that the value is
270
+ // being set from uinitialized to initialized via this function.
271
+ //
272
+ // It needs to be done in the order to ensure the safety of this function
273
+ // across threads, this function will mutate the value_set to be
274
+ // AtomicState::Done.
260
275
unsafe fn set_value ( & self , value : T ) {
261
- unsafe {
262
- self . value . with_mut ( |ptr| ( * ptr) . as_mut_ptr ( ) . write ( value) ) ;
263
- }
276
+ self . value . with_mut ( |ptr| ( * ptr) . as_mut_ptr ( ) . write ( value) ) ;
277
+
278
+ // Store that we're done in Release so any `Acquire` can see
279
+ self . value_set
280
+ . store ( AtomicState :: Done as _ , Ordering :: Release ) ;
264
281
265
282
// notify the waiting wakers that the value is set
266
283
self . notify . notify_waiters ( ) ;
@@ -282,19 +299,35 @@ impl<T> SetOnce<T> {
282
299
return Err ( SetError :: AlreadyInitializedError ( value) ) ;
283
300
}
284
301
285
- // Using release ordering so any threads that read a true from this
286
- // atomic is able to read the value we just stored.
287
- if !self . value_set . swap ( true , Ordering :: Release ) {
288
- // SAFETY: We are swapping the value_set AtomicBool from FALSE to
289
- // TRUE with it being previously false and followed by that we are
290
- // initializing the unsafe Cell field with the value
291
- unsafe {
292
- self . set_value ( value) ;
293
- }
302
+ // to capture any writes that are done with `Ordering::Release`
303
+ let ordering = Ordering :: Acquire ;
294
304
295
- Ok ( ( ) )
296
- } else {
297
- Err ( SetError :: InitializingError ( value) )
305
+ match self . value_set . compare_exchange (
306
+ // we expect the set to be uninitialized
307
+ AtomicState :: Empty as _ ,
308
+ // we want to lock the Set
309
+ AtomicState :: Setting as _ ,
310
+ ordering,
311
+ ordering,
312
+ ) {
313
+ Ok ( _) => {
314
+ // SAFETY: We changed the state from EMPTY to SETTING,
315
+ // this declares the set to be currently running
316
+ unsafe {
317
+ self . set_value ( value) ;
318
+ }
319
+
320
+ Ok ( ( ) )
321
+ }
322
+ Err ( true_val) => {
323
+ // The value is being currently set in another thread
324
+ if true_val == AtomicState :: Setting as _ {
325
+ Err ( SetError :: InitializingError ( value) )
326
+ } else {
327
+ // here means true_val is either Done or higher
328
+ Err ( SetError :: AlreadyInitializedError ( value) )
329
+ }
330
+ }
298
331
}
299
332
}
300
333
@@ -304,9 +337,9 @@ impl<T> SetOnce<T> {
304
337
if self . initialized ( ) {
305
338
// Since we have taken ownership of self, its drop implementation
306
339
// will be called by the end of this function, to prevent a double
307
- // free we will set the value_set to false so that the drop
340
+ // free we will set the value_set to EMPTY so that the drop
308
341
// implementation does not try to drop the value again.
309
- * self . value_set . get_mut ( ) = false ;
342
+ * self . value_set . get_mut ( ) = AtomicState :: Empty as _ ;
310
343
311
344
// SAFETY: The SetOnce is currently initialized, we can assume the
312
345
// value is initialized and return that, when we return the value
@@ -337,7 +370,7 @@ impl<T> SetOnce<T> {
337
370
// (of type T) across threads.
338
371
unsafe impl < T : Sync + Send > Sync for SetOnce < T > { }
339
372
340
- // Access to SetOnce's value is guarded by the Atomic boolean flag
373
+ // Access to SetOnce's value is guarded by the AtomicUsize state
341
374
// and atomic operations on `value_set`, so as long as T itself is Send
342
375
// it's safe to send it to another thread
343
376
unsafe impl < T : Send > Send for SetOnce < T > { }
0 commit comments