diff options
| author | bors <bors@rust-lang.org> | 2018-09-05 00:37:03 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-09-05 00:37:03 +0000 |
| commit | f68b7cc59896427d378c9e3bae6f5fd7a1f1fad9 (patch) | |
| tree | e7c43bb48abe1a3fe6c8a8d7fa2de19f5b644958 /src/libstd | |
| parent | 1c2e17f4e3a2070a7f703f51e29c1c388ef703b6 (diff) | |
| parent | e1bd0e7b4e9009ada545580b2698a11631f1f597 (diff) | |
| download | rust-f68b7cc59896427d378c9e3bae6f5fd7a1f1fad9.tar.gz rust-f68b7cc59896427d378c9e3bae6f5fd7a1f1fad9.zip | |
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.
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/sync/once.rs | 73 |
1 files changed, 59 insertions, 14 deletions
diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index f258ff073e2..ce54726baee 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -221,13 +221,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); @@ -282,13 +278,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); @@ -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 // using `call_once` (this isn't exactly a trivial or small implementation). // @@ -312,6 +353,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 { |
