]> git.lizzy.rs Git - rust.git/commitdiff
more spin-loop-tests
authorRalf Jung <post@ralfj.de>
Sun, 5 Jun 2022 18:31:44 +0000 (14:31 -0400)
committerRalf Jung <post@ralfj.de>
Sun, 5 Jun 2022 18:36:49 +0000 (14:36 -0400)
tests/pass/concurrency/spin_loops.rs
tests/pass/concurrency/spin_loops.stderr [new file with mode: 0644]

index 4229e54406815c1c0f1fe840f8a17b286857bc72..a6fceb03638dd873dc404f4a27ec00e38cf85d8b 100644 (file)
@@ -1,16 +1,17 @@
 // ignore-windows: Concurrency on Windows is not supported yet.
 
 use std::thread;
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
+use std::sync::mpsc;
+use std::cell::Cell;
 
-static FLAG: AtomicUsize = AtomicUsize::new(0);
+/// When a thread yields, Miri's scheduler used to pick the thread with the lowest ID
+/// that can run. IDs are assigned in thread creation order.
+/// This means we could make 2 threads infinitely ping-pong with each other while
+/// really there is a 3rd thread that we should schedule to make progress.
+fn two_player_ping_pong() {
+    static FLAG: AtomicUsize = AtomicUsize::new(0);
 
-// When a thread yields, Miri's scheduler used to pick the thread with the lowest ID
-// that can run. IDs are assigned in thread creation order.
-// This means we could make 2 threads infinitely ping-pong with each other while
-// really there is a 3rd thread that we should schedule to make progress.
-
-fn main() {
     let waiter1 = thread::spawn(|| {
         while FLAG.load(Ordering::Acquire) == 0 {
             // spin and wait
@@ -31,3 +32,51 @@ fn main() {
     waiter2.join().unwrap();
     progress.join().unwrap();
 }
+
+/// Based on a test by @jethrogb.
+fn launcher() {
+    static THREAD2_LAUNCHED: AtomicBool = AtomicBool::new(false);
+
+    for _ in 0..10 {
+        let (tx, rx) = mpsc::sync_channel(0);
+        THREAD2_LAUNCHED.store(false, Ordering::SeqCst);
+
+        let jh = thread::spawn(move || {
+            struct RecvOnDrop(Cell<Option<mpsc::Receiver<()>>>);
+
+            impl Drop for RecvOnDrop {
+                fn drop(&mut self) {
+                    let rx = self.0.take().unwrap();
+                    while !THREAD2_LAUNCHED.load(Ordering::SeqCst) {
+                        thread::yield_now();
+                    }
+                    rx.recv().unwrap();
+                }
+            }
+
+            let tl_rx: RecvOnDrop = RecvOnDrop(Cell::new(None));
+            tl_rx.0.set(Some(rx));
+        });
+
+        let tx_clone = tx.clone();
+        let jh2 = thread::spawn(move || {
+            THREAD2_LAUNCHED.store(true, Ordering::SeqCst);
+            jh.join().unwrap();
+            tx_clone.send(()).expect_err(
+                "Expecting channel to be closed because thread 1 TLS destructors must've run",
+            );
+        });
+
+        while !THREAD2_LAUNCHED.load(Ordering::SeqCst) {
+            thread::yield_now();
+        }
+        thread::yield_now();
+        tx.send(()).expect("Expecting channel to be live because thread 2 must block on join");
+        jh2.join().unwrap();
+    }
+}
+
+fn main() {
+    two_player_ping_pong();
+    launcher();
+}
diff --git a/tests/pass/concurrency/spin_loops.stderr b/tests/pass/concurrency/spin_loops.stderr
new file mode 100644 (file)
index 0000000..0367651
--- /dev/null
@@ -0,0 +1,2 @@
+warning: thread support is experimental and incomplete: weak memory effects are not emulated.
+