-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Allow to check if sync::Once is already initialized #53027
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -219,13 +219,9 @@ impl Once { | |
/// [poison]: struct.Mutex.html#poisoning | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub fn call_once<F>(&self, f: F) where F: FnOnce() { | ||
// Fast path, just see if we've completed initialization. | ||
// An `Acquire` load is enough because that makes all the initialization | ||
// operations visible to us. The cold path uses SeqCst consistently | ||
// 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 | ||
// Fast path check | ||
if self.is_completed() { | ||
return; | ||
} | ||
|
||
let mut f = Some(f); | ||
|
@@ -280,13 +276,9 @@ impl Once { | |
/// ``` | ||
#[unstable(feature = "once_poison", issue = "33577")] | ||
pub fn call_once_force<F>(&self, f: F) where F: FnOnce(&OnceState) { | ||
// same as above, just with a different parameter to `call_inner`. | ||
// An `Acquire` load is enough because that makes all the initialization | ||
// operations visible to us. The cold path uses SeqCst consistently | ||
// 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 | ||
// Fast path check | ||
if self.is_completed() { | ||
return; | ||
} | ||
|
||
let mut f = Some(f); | ||
|
@@ -295,6 +287,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")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this issue number wrong? Issue #42 seems totally irrelevant to this code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a placeholder, there's no tracking issue for this yet There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I just found that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As soon as @rust-lang/libs decides that we indeed want this feature and creates a tracking issue, I'll update the PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the explanation! |
||
pub fn is_completed(&self) -> bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @eddyb is that documented in the api guidelines or somewhere similar? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in #54662 |
||
// 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 | ||
// using `call_once` (this isn't exactly a trivial or small implementation). | ||
// | ||
|
@@ -310,6 +351,10 @@ impl Once { | |
fn call_inner(&self, | ||
ignore_poisoning: 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); | ||
|
||
'outer: loop { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think these would normally be written as
assert!(INIT.is_completed())
/assert!(!INIT.is_completed())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for boolean-returning methods writing an
assert_eq!
in docs specifically helps a bit with readability. Here's an example from result:https://doc.rust-lang.org/std/result/enum.Result.html#method.is_ok