about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-05 00:37:03 +0000
committerbors <bors@rust-lang.org>2018-09-05 00:37:03 +0000
commitf68b7cc59896427d378c9e3bae6f5fd7a1f1fad9 (patch)
treee7c43bb48abe1a3fe6c8a8d7fa2de19f5b644958 /src/libstd
parent1c2e17f4e3a2070a7f703f51e29c1c388ef703b6 (diff)
parente1bd0e7b4e9009ada545580b2698a11631f1f597 (diff)
downloadrust-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.rs73
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 {