1
1
// ignore-windows: Concurrency on Windows is not supported yet.
2
2
3
3
use 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 ;
5
7
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 ) ;
7
14
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 ( ) {
14
15
let waiter1 = thread:: spawn ( || {
15
16
while FLAG . load ( Ordering :: Acquire ) == 0 {
16
17
// spin and wait
@@ -31,3 +32,51 @@ fn main() {
31
32
waiter2. join ( ) . unwrap ( ) ;
32
33
progress. join ( ) . unwrap ( ) ;
33
34
}
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