1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#![deny(warnings, rust_2018_idioms)]
use loom;
use loom::sync::atomic::AtomicUsize;
use loom::sync::{CausalCell, Mutex};
use loom::thread;
use std::rc::Rc;
use std::sync::atomic::Ordering::SeqCst;
#[test]
fn mutex_enforces_mutal_exclusion() {
loom::model(|| {
let data = Rc::new((Mutex::new(0), AtomicUsize::new(0)));
let ths: Vec<_> = (0..2)
.map(|_| {
let data = data.clone();
thread::spawn(move || {
let mut locked = data.0.lock().unwrap();
let prev = data.1.fetch_add(1, SeqCst);
assert_eq!(prev, *locked);
*locked += 1;
})
})
.collect();
for th in ths {
th.join().unwrap();
}
let locked = data.0.lock().unwrap();
assert_eq!(*locked, data.1.load(SeqCst));
});
}
#[test]
fn mutex_establishes_seq_cst() {
loom::model(|| {
struct Data {
cell: CausalCell<usize>,
flag: Mutex<bool>,
}
let data = Rc::new(Data {
cell: CausalCell::new(0),
flag: Mutex::new(false),
});
{
let data = data.clone();
thread::spawn(move || {
unsafe { data.cell.with_mut(|v| *v = 1) };
*data.flag.lock().unwrap() = true;
});
}
let flag = *data.flag.lock().unwrap();
if flag {
let v = unsafe { data.cell.with(|v| *v) };
assert_eq!(v, 1);
}
});
}
#[cfg(feature = "futures")]
#[test]
fn waking_mutex_waiter_shouldnt_unpark() {
use loom::future::block_on;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, Waker};
struct MyFuture {
poll_ctr: usize,
mtx: Rc<Mutex<Option<Waker>>>,
}
impl Future for MyFuture {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = self.as_mut().get_mut();
this.poll_ctr += 1;
// Lock and set th2's waker.
{
let mut lock = this.mtx.lock().unwrap();
*lock = Some(cx.waker().clone());
}
// Lock again. In some executions, th2 will yield to th1 who will
// then wake th2 with the waker set above. However, waking must not
// unpark us in this case, since we are parked on a mutex and not
// parked from returning `Poll::Pending`. If we were to unpark, we
// would successfully acquire the lock even though th1 already holds
// it.
this.mtx.lock().unwrap();
// Poll `MyFuture` twice so we exercise both `Poll::Pending` and
// `Poll::Ready` paths.
if this.poll_ctr == 1 {
return Poll::Pending;
} else if this.poll_ctr == 2 {
return Poll::Ready(());
} else {
panic!("poll_ctr is not 1 or 2: {}", this.poll_ctr);
}
}
}
loom::model(|| {
let mtx1: Rc<Mutex<Option<Waker>>> = Rc::new(Mutex::new(None));
let mtx2 = mtx1.clone();
let th1 = thread::spawn(move || {
let mut wake_ctr = 0;
loop {
// Wait until th2 sets its waker, then lock + wake th2.
{
let mut lock = mtx1.lock().unwrap();
let waker = lock.take();
if let Some(waker) = waker {
// We want to wake th2 twice.
wake_ctr += 1;
// Bug causes th2 to run and acquire lock despite th1
// already holding the lock.
waker.wake();
// Yield so th2 can run and try to lock again.
thread::yield_now();
if wake_ctr == 2 {
break;
}
}
}
// th2 hasn't set their waker yet, so just yield.
thread::yield_now();
}
});
let th2 = thread::spawn(move || {
let fut = MyFuture {
poll_ctr: 0,
mtx: mtx2,
};
block_on(fut);
});
th1.join().unwrap();
th2.join().unwrap();
});
}