11// ignore-windows: Concurrency on Windows is not supported yet.
22
33use std:: thread;
4- use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
4+ use std:: sync:: atomic:: { AtomicBool , AtomicUsize , Ordering } ;
5+ use std:: sync:: mpsc;
6+ use std:: cell:: Cell ;
57
6- static FLAG : AtomicUsize = AtomicUsize :: new ( 0 ) ;
8+ /// When a thread yields, Miri's scheduler used to pick the thread with the lowest ID
9+ /// that can run. IDs are assigned in thread creation order.
10+ /// This means we could make 2 threads infinitely ping-pong with each other while
11+ /// really there is a 3rd thread that we should schedule to make progress.
12+ fn two_player_ping_pong ( ) {
13+ static FLAG : AtomicUsize = AtomicUsize :: new ( 0 ) ;
714
8- // When a thread yields, Miri's scheduler used to pick the thread with the lowest ID
9- // that can run. IDs are assigned in thread creation order.
10- // This means we could make 2 threads infinitely ping-pong with each other while
11- // really there is a 3rd thread that we should schedule to make progress.
12-
13- fn main ( ) {
1415 let waiter1 = thread:: spawn ( || {
1516 while FLAG . load ( Ordering :: Acquire ) == 0 {
1617 // spin and wait
@@ -31,3 +32,51 @@ fn main() {
3132 waiter2. join ( ) . unwrap ( ) ;
3233 progress. join ( ) . unwrap ( ) ;
3334}
35+
36+ /// Based on a test by @jethrogb.
37+ fn launcher ( ) {
38+ static THREAD2_LAUNCHED : AtomicBool = AtomicBool :: new ( false ) ;
39+
40+ for _ in 0 ..10 {
41+ let ( tx, rx) = mpsc:: sync_channel ( 0 ) ;
42+ THREAD2_LAUNCHED . store ( false , Ordering :: SeqCst ) ;
43+
44+ let jh = thread:: spawn ( move || {
45+ struct RecvOnDrop ( Cell < Option < mpsc:: Receiver < ( ) > > > ) ;
46+
47+ impl Drop for RecvOnDrop {
48+ fn drop ( & mut self ) {
49+ let rx = self . 0 . take ( ) . unwrap ( ) ;
50+ while !THREAD2_LAUNCHED . load ( Ordering :: SeqCst ) {
51+ thread:: yield_now ( ) ;
52+ }
53+ rx. recv ( ) . unwrap ( ) ;
54+ }
55+ }
56+
57+ let tl_rx: RecvOnDrop = RecvOnDrop ( Cell :: new ( None ) ) ;
58+ tl_rx. 0 . set ( Some ( rx) ) ;
59+ } ) ;
60+
61+ let tx_clone = tx. clone ( ) ;
62+ let jh2 = thread:: spawn ( move || {
63+ THREAD2_LAUNCHED . store ( true , Ordering :: SeqCst ) ;
64+ jh. join ( ) . unwrap ( ) ;
65+ tx_clone. send ( ( ) ) . expect_err (
66+ "Expecting channel to be closed because thread 1 TLS destructors must've run" ,
67+ ) ;
68+ } ) ;
69+
70+ while !THREAD2_LAUNCHED . load ( Ordering :: SeqCst ) {
71+ thread:: yield_now ( ) ;
72+ }
73+ thread:: yield_now ( ) ;
74+ tx. send ( ( ) ) . expect ( "Expecting channel to be live because thread 2 must block on join" ) ;
75+ jh2. join ( ) . unwrap ( ) ;
76+ }
77+ }
78+
79+ fn main ( ) {
80+ two_player_ping_pong ( ) ;
81+ launcher ( ) ;
82+ }
0 commit comments