about summary refs log tree commit diff
path: root/library/std/src/sync/once
diff options
context:
space:
mode:
authorLzu Tao <taolzu@gmail.com>2020-08-27 13:45:01 +0000
committerLzu Tao <taolzu@gmail.com>2020-08-31 02:56:59 +0000
commita4e926daeeaedc9178846711daf1f4cb6ce505fb (patch)
tree0c830f716f6f5ad17736d459f5de9b9199006d54 /library/std/src/sync/once
parentdb6cbfc49ca655739ba8caae43ebd7c77c8a1179 (diff)
std: move "mod tests/benches" to separate files
Also doing fmt inplace as requested.
Diffstat (limited to 'library/std/src/sync/once')
-rw-r--r--library/std/src/sync/once/tests.rs116
1 files changed, 116 insertions, 0 deletions
diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/once/tests.rs
new file mode 100644
index 00000000000..fae2752526e
--- /dev/null
+++ b/library/std/src/sync/once/tests.rs
@@ -0,0 +1,116 @@
+use super::Once;
+use crate::panic;
+use crate::sync::mpsc::channel;
+use crate::thread;
+
+#[test]
+fn smoke_once() {
+    static O: Once = Once::new();
+    let mut a = 0;
+    O.call_once(|| a += 1);
+    assert_eq!(a, 1);
+    O.call_once(|| a += 1);
+    assert_eq!(a, 1);
+}
+
+#[test]
+fn stampede_once() {
+    static O: Once = Once::new();
+    static mut RUN: bool = false;
+
+    let (tx, rx) = channel();
+    for _ in 0..10 {
+        let tx = tx.clone();
+        thread::spawn(move || {
+            for _ in 0..4 {
+                thread::yield_now()
+            }
+            unsafe {
+                O.call_once(|| {
+                    assert!(!RUN);
+                    RUN = true;
+                });
+                assert!(RUN);
+            }
+            tx.send(()).unwrap();
+        });
+    }
+
+    unsafe {
+        O.call_once(|| {
+            assert!(!RUN);
+            RUN = true;
+        });
+        assert!(RUN);
+    }
+
+    for _ in 0..10 {
+        rx.recv().unwrap();
+    }
+}
+
+#[test]
+fn poison_bad() {
+    static O: Once = Once::new();
+
+    // poison the once
+    let t = panic::catch_unwind(|| {
+        O.call_once(|| panic!());
+    });
+    assert!(t.is_err());
+
+    // poisoning propagates
+    let t = panic::catch_unwind(|| {
+        O.call_once(|| {});
+    });
+    assert!(t.is_err());
+
+    // we can subvert poisoning, however
+    let mut called = false;
+    O.call_once_force(|p| {
+        called = true;
+        assert!(p.poisoned())
+    });
+    assert!(called);
+
+    // once any success happens, we stop propagating the poison
+    O.call_once(|| {});
+}
+
+#[test]
+fn wait_for_force_to_finish() {
+    static O: Once = Once::new();
+
+    // poison the once
+    let t = panic::catch_unwind(|| {
+        O.call_once(|| panic!());
+    });
+    assert!(t.is_err());
+
+    // make sure someone's waiting inside the once via a force
+    let (tx1, rx1) = channel();
+    let (tx2, rx2) = channel();
+    let t1 = thread::spawn(move || {
+        O.call_once_force(|p| {
+            assert!(p.poisoned());
+            tx1.send(()).unwrap();
+            rx2.recv().unwrap();
+        });
+    });
+
+    rx1.recv().unwrap();
+
+    // put another waiter on the once
+    let t2 = thread::spawn(|| {
+        let mut called = false;
+        O.call_once(|| {
+            called = true;
+        });
+        assert!(!called);
+    });
+
+    tx2.send(()).unwrap();
+
+    assert!(t1.join().is_ok());
+    assert!(t2.join().is_ok());
+}