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:
commit
f68b7cc598
1 changed files with 59 additions and 14 deletions
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue