From 34b359be1ec89383baff1dba5f74f5bfe8beedd7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 Jun 2022 14:31:44 -0400 Subject: [PATCH] more spin-loop-tests --- tests/pass/concurrency/spin_loops.rs | 65 +++++++++++++++++++++--- tests/pass/concurrency/spin_loops.stderr | 2 + 2 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 tests/pass/concurrency/spin_loops.stderr diff --git a/tests/pass/concurrency/spin_loops.rs b/tests/pass/concurrency/spin_loops.rs index 4229e5440681..a6fceb03638d 100644 --- a/tests/pass/concurrency/spin_loops.rs +++ b/tests/pass/concurrency/spin_loops.rs @@ -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>>); + + 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 index 000000000000..03676519d4f1 --- /dev/null +++ b/tests/pass/concurrency/spin_loops.stderr @@ -0,0 +1,2 @@ +warning: thread support is experimental and incomplete: weak memory effects are not emulated. +