1
Fork 0

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:

8127a81976/src/lib.rs (L423-L429)

8127a81976/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.
This commit is contained in:
bors 2018-09-05 00:37:03 +00:00
commit f68b7cc598

View file

@ -221,13 +221,9 @@ impl Once {
/// [poison]: struct.Mutex.html#poisoning /// [poison]: struct.Mutex.html#poisoning
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn call_once<F>(&self, f: F) where F: FnOnce() { pub fn call_once<F>(&self, f: F) where F: FnOnce() {
// Fast path, just see if we've completed initialization. // Fast path check
// An `Acquire` load is enough because that makes all the initialization if self.is_completed() {
// operations visible to us. The cold path uses SeqCst consistently return;
// because the performance difference really does not matter there,
// and SeqCst minimizes the chances of something going wrong.
if self.state.load(Ordering::Acquire) == COMPLETE {
return
} }
let mut f = Some(f); let mut f = Some(f);
@ -282,13 +278,9 @@ impl Once {
/// ``` /// ```
#[unstable(feature = "once_poison", issue = "33577")] #[unstable(feature = "once_poison", issue = "33577")]
pub fn call_once_force<F>(&self, f: F) where F: FnOnce(&OnceState) { pub fn call_once_force<F>(&self, f: F) where F: FnOnce(&OnceState) {
// same as above, just with a different parameter to `call_inner`. // Fast path check
// An `Acquire` load is enough because that makes all the initialization if self.is_completed() {
// operations visible to us. The cold path uses SeqCst consistently return;
// because the performance difference really does not matter there,
// and SeqCst minimizes the chances of something going wrong.
if self.state.load(Ordering::Acquire) == COMPLETE {
return
} }
let mut f = Some(f); let mut f = Some(f);
@ -297,6 +289,55 @@ impl Once {
}); });
} }
/// Returns true if some `call_once` call has completed
/// successfuly. Specifically, `is_completed` will return false in
/// the following situtations:
/// * `call_once` was not called at all,
/// * `call_once` was called, but has not yet completed,
/// * the `Once` instance is poisoned
///
/// It is also possible that immediately after `is_completed`
/// returns false, some other thread finishes executing
/// `call_once`.
///
/// # Examples
///
/// ```
/// #![feature(once_is_completed)]
/// use std::sync::Once;
///
/// static INIT: Once = Once::new();
///
/// assert_eq!(INIT.is_completed(), false);
/// INIT.call_once(|| {
/// assert_eq!(INIT.is_completed(), false);
/// });
/// assert_eq!(INIT.is_completed(), true);
/// ```
///
/// ```
/// #![feature(once_is_completed)]
/// use std::sync::Once;
/// use std::thread;
///
/// static INIT: Once = Once::new();
///
/// assert_eq!(INIT.is_completed(), false);
/// let handle = thread::spawn(|| {
/// INIT.call_once(|| panic!());
/// });
/// assert!(handle.join().is_err());
/// assert_eq!(INIT.is_completed(), false);
/// ```
#[unstable(feature = "once_is_completed", issue = "42")]
pub fn is_completed(&self) -> bool {
// An `Acquire` load is enough because that makes all the initialization
// operations visible to us, and, this being a fast path, weaker
// ordering helps with performance. This `Acquire` synchronizes with
// `SeqCst` operations on the slow path.
self.state.load(Ordering::Acquire) == COMPLETE
}
// This is a non-generic function to reduce the monomorphization cost of // This is a non-generic function to reduce the monomorphization cost of
// using `call_once` (this isn't exactly a trivial or small implementation). // using `call_once` (this isn't exactly a trivial or small implementation).
// //
@ -312,6 +353,10 @@ impl Once {
fn call_inner(&self, fn call_inner(&self,
ignore_poisoning: bool, ignore_poisoning: bool,
init: &mut dyn FnMut(bool)) { init: &mut dyn FnMut(bool)) {
// This cold path uses SeqCst consistently because the
// performance difference really does not matter there, and
// SeqCst minimizes the chances of something going wrong.
let mut state = self.state.load(Ordering::SeqCst); let mut state = self.state.load(Ordering::SeqCst);
'outer: loop { 'outer: loop {