Skip to content

Commit f68b7cc

Browse files
committed
Auto merge of #53027 - matklad:once_is_completed, r=alexcrichton
Allow to check if sync::Once is already initialized Hi! I propose to expose a way to check if a `Once` instance is initialized. I need it in `once_cell`. `OnceCell` is effetively a pair of `(Once, UnsafeCell<Option<T>>)`, which can set the `T` only once. Because I can't check if `Once` is initialized, I am forced to add an indirection and check the value of ptr instead: https://github.com./matklad/once_cell/blob/8127a81976c3f2f4c0860562c3f14647ebc025c0/src/lib.rs#L423-L429 https://github.com./matklad/once_cell/blob/8127a81976c3f2f4c0860562c3f14647ebc025c0/src/lib.rs#L457-L461 The `parking_lot`'s version of `Once` exposes the state as an enum: https://docs.rs/parking_lot/0.6.3/parking_lot/struct.Once.html#method.state. I suggest, for now, just to add a simple `bool` function: this fits my use-case perfectly, exposes less implementation details, and is forward-compatible with more fine-grained state checking.
2 parents 1c2e17f + e1bd0e7 commit f68b7cc

File tree

1 file changed

+59
-14
lines changed

1 file changed

+59
-14
lines changed

src/libstd/sync/once.rs

+59-14
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,9 @@ impl Once {
221221
/// [poison]: struct.Mutex.html#poisoning
222222
#[stable(feature = "rust1", since = "1.0.0")]
223223
pub fn call_once<F>(&self, f: F) where F: FnOnce() {
224-
// Fast path, just see if we've completed initialization.
225-
// An `Acquire` load is enough because that makes all the initialization
226-
// operations visible to us. The cold path uses SeqCst consistently
227-
// because the performance difference really does not matter there,
228-
// and SeqCst minimizes the chances of something going wrong.
229-
if self.state.load(Ordering::Acquire) == COMPLETE {
230-
return
224+
// Fast path check
225+
if self.is_completed() {
226+
return;
231227
}
232228

233229
let mut f = Some(f);
@@ -282,13 +278,9 @@ impl Once {
282278
/// ```
283279
#[unstable(feature = "once_poison", issue = "33577")]
284280
pub fn call_once_force<F>(&self, f: F) where F: FnOnce(&OnceState) {
285-
// same as above, just with a different parameter to `call_inner`.
286-
// An `Acquire` load is enough because that makes all the initialization
287-
// operations visible to us. The cold path uses SeqCst consistently
288-
// because the performance difference really does not matter there,
289-
// and SeqCst minimizes the chances of something going wrong.
290-
if self.state.load(Ordering::Acquire) == COMPLETE {
291-
return
281+
// Fast path check
282+
if self.is_completed() {
283+
return;
292284
}
293285

294286
let mut f = Some(f);
@@ -297,6 +289,55 @@ impl Once {
297289
});
298290
}
299291

292+
/// Returns true if some `call_once` call has completed
293+
/// successfuly. Specifically, `is_completed` will return false in
294+
/// the following situtations:
295+
/// * `call_once` was not called at all,
296+
/// * `call_once` was called, but has not yet completed,
297+
/// * the `Once` instance is poisoned
298+
///
299+
/// It is also possible that immediately after `is_completed`
300+
/// returns false, some other thread finishes executing
301+
/// `call_once`.
302+
///
303+
/// # Examples
304+
///
305+
/// ```
306+
/// #![feature(once_is_completed)]
307+
/// use std::sync::Once;
308+
///
309+
/// static INIT: Once = Once::new();
310+
///
311+
/// assert_eq!(INIT.is_completed(), false);
312+
/// INIT.call_once(|| {
313+
/// assert_eq!(INIT.is_completed(), false);
314+
/// });
315+
/// assert_eq!(INIT.is_completed(), true);
316+
/// ```
317+
///
318+
/// ```
319+
/// #![feature(once_is_completed)]
320+
/// use std::sync::Once;
321+
/// use std::thread;
322+
///
323+
/// static INIT: Once = Once::new();
324+
///
325+
/// assert_eq!(INIT.is_completed(), false);
326+
/// let handle = thread::spawn(|| {
327+
/// INIT.call_once(|| panic!());
328+
/// });
329+
/// assert!(handle.join().is_err());
330+
/// assert_eq!(INIT.is_completed(), false);
331+
/// ```
332+
#[unstable(feature = "once_is_completed", issue = "42")]
333+
pub fn is_completed(&self) -> bool {
334+
// An `Acquire` load is enough because that makes all the initialization
335+
// operations visible to us, and, this being a fast path, weaker
336+
// ordering helps with performance. This `Acquire` synchronizes with
337+
// `SeqCst` operations on the slow path.
338+
self.state.load(Ordering::Acquire) == COMPLETE
339+
}
340+
300341
// This is a non-generic function to reduce the monomorphization cost of
301342
// using `call_once` (this isn't exactly a trivial or small implementation).
302343
//
@@ -312,6 +353,10 @@ impl Once {
312353
fn call_inner(&self,
313354
ignore_poisoning: bool,
314355
init: &mut dyn FnMut(bool)) {
356+
357+
// This cold path uses SeqCst consistently because the
358+
// performance difference really does not matter there, and
359+
// SeqCst minimizes the chances of something going wrong.
315360
let mut state = self.state.load(Ordering::SeqCst);
316361

317362
'outer: loop {

0 commit comments

Comments
 (0)