// 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
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();
+}