]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs
Auto merge of #105323 - cjgillot:simplify-const-prop, r=davidtwco
[rust.git] / src / tools / miri / tests / pass-dep / concurrency / linux-futex.rs
1 //@only-target-linux
2 //@compile-flags: -Zmiri-disable-isolation
3
4 use std::mem::MaybeUninit;
5 use std::ptr;
6 use std::sync::atomic::AtomicI32;
7 use std::sync::atomic::Ordering;
8 use std::thread;
9 use std::time::{Duration, Instant};
10
11 fn wake_nobody() {
12     let futex = 0;
13
14     // Wake 1 waiter. Expect zero waiters woken up, as nobody is waiting.
15     unsafe {
16         assert_eq!(libc::syscall(libc::SYS_futex, &futex as *const i32, libc::FUTEX_WAKE, 1), 0);
17     }
18
19     // Same, but without omitting the unused arguments.
20     unsafe {
21         assert_eq!(
22             libc::syscall(
23                 libc::SYS_futex,
24                 &futex as *const i32,
25                 libc::FUTEX_WAKE,
26                 1,
27                 ptr::null::<libc::timespec>(),
28                 0usize,
29                 0,
30             ),
31             0,
32         );
33     }
34 }
35
36 fn wake_dangling() {
37     let futex = Box::new(0);
38     let ptr: *const i32 = &*futex;
39     drop(futex);
40
41     // Wake 1 waiter. Expect zero waiters woken up, as nobody is waiting.
42     unsafe {
43         assert_eq!(libc::syscall(libc::SYS_futex, ptr, libc::FUTEX_WAKE, 1), 0);
44     }
45 }
46
47 fn wait_wrong_val() {
48     let futex: i32 = 123;
49
50     // Only wait if the futex value is 456.
51     unsafe {
52         assert_eq!(
53             libc::syscall(
54                 libc::SYS_futex,
55                 &futex as *const i32,
56                 libc::FUTEX_WAIT,
57                 456,
58                 ptr::null::<libc::timespec>(),
59             ),
60             -1,
61         );
62         assert_eq!(*libc::__errno_location(), libc::EAGAIN);
63     }
64 }
65
66 fn wait_timeout() {
67     let start = Instant::now();
68
69     let futex: i32 = 123;
70
71     // Wait for 200ms, with nobody waking us up early.
72     unsafe {
73         assert_eq!(
74             libc::syscall(
75                 libc::SYS_futex,
76                 &futex as *const i32,
77                 libc::FUTEX_WAIT,
78                 123,
79                 &libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 },
80             ),
81             -1,
82         );
83         assert_eq!(*libc::__errno_location(), libc::ETIMEDOUT);
84     }
85
86     assert!((200..1000).contains(&start.elapsed().as_millis()));
87 }
88
89 fn wait_absolute_timeout() {
90     let start = Instant::now();
91
92     // Get the current monotonic timestamp as timespec.
93     let mut timeout = unsafe {
94         let mut now: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
95         assert_eq!(libc::clock_gettime(libc::CLOCK_MONOTONIC, now.as_mut_ptr()), 0);
96         now.assume_init()
97     };
98
99     // Add 200ms.
100     timeout.tv_nsec += 200_000_000;
101     if timeout.tv_nsec > 1_000_000_000 {
102         timeout.tv_nsec -= 1_000_000_000;
103         timeout.tv_sec += 1;
104     }
105
106     let futex: i32 = 123;
107
108     // Wait for 200ms from now, with nobody waking us up early.
109     unsafe {
110         assert_eq!(
111             libc::syscall(
112                 libc::SYS_futex,
113                 &futex as *const i32,
114                 libc::FUTEX_WAIT_BITSET,
115                 123,
116                 &timeout,
117                 0usize,
118                 u32::MAX,
119             ),
120             -1,
121         );
122         assert_eq!(*libc::__errno_location(), libc::ETIMEDOUT);
123     }
124
125     assert!((200..1000).contains(&start.elapsed().as_millis()));
126 }
127
128 fn wait_wake() {
129     let start = Instant::now();
130
131     static mut FUTEX: i32 = 0;
132
133     let t = thread::spawn(move || {
134         thread::sleep(Duration::from_millis(200));
135         unsafe {
136             assert_eq!(
137                 libc::syscall(
138                     libc::SYS_futex,
139                     &FUTEX as *const i32,
140                     libc::FUTEX_WAKE,
141                     10, // Wake up at most 10 threads.
142                 ),
143                 1, // Woken up one thread.
144             );
145         }
146     });
147
148     unsafe {
149         assert_eq!(
150             libc::syscall(
151                 libc::SYS_futex,
152                 &FUTEX as *const i32,
153                 libc::FUTEX_WAIT,
154                 0,
155                 ptr::null::<libc::timespec>(),
156             ),
157             0,
158         );
159     }
160
161     assert!((200..1000).contains(&start.elapsed().as_millis()));
162     t.join().unwrap();
163 }
164
165 fn wait_wake_bitset() {
166     let start = Instant::now();
167
168     static mut FUTEX: i32 = 0;
169
170     let t = thread::spawn(move || {
171         thread::sleep(Duration::from_millis(200));
172         unsafe {
173             assert_eq!(
174                 libc::syscall(
175                     libc::SYS_futex,
176                     &FUTEX as *const i32,
177                     libc::FUTEX_WAKE_BITSET,
178                     10, // Wake up at most 10 threads.
179                     ptr::null::<libc::timespec>(),
180                     0usize,
181                     0b1001, // bitset
182                 ),
183                 0, // Didn't match any thread.
184             );
185         }
186         thread::sleep(Duration::from_millis(200));
187         unsafe {
188             assert_eq!(
189                 libc::syscall(
190                     libc::SYS_futex,
191                     &FUTEX as *const i32,
192                     libc::FUTEX_WAKE_BITSET,
193                     10, // Wake up at most 10 threads.
194                     ptr::null::<libc::timespec>(),
195                     0usize,
196                     0b0110, // bitset
197                 ),
198                 1, // Woken up one thread.
199             );
200         }
201     });
202
203     unsafe {
204         assert_eq!(
205             libc::syscall(
206                 libc::SYS_futex,
207                 &FUTEX as *const i32,
208                 libc::FUTEX_WAIT_BITSET,
209                 0,
210                 ptr::null::<libc::timespec>(),
211                 0usize,
212                 0b0100, // bitset
213             ),
214             0,
215         );
216     }
217
218     assert!((400..1000).contains(&start.elapsed().as_millis()));
219     t.join().unwrap();
220 }
221
222 fn concurrent_wait_wake() {
223     const FREE: i32 = 0;
224     const HELD: i32 = 1;
225
226     static FUTEX: AtomicI32 = AtomicI32::new(0);
227     static mut DATA: i32 = 0;
228     static WOKEN: AtomicI32 = AtomicI32::new(0);
229
230     let rounds = 50;
231     for _ in 0..rounds {
232         unsafe { DATA = 0 }; // Reset
233         // Suppose the main thread is holding a lock implemented using futex...
234         FUTEX.store(HELD, Ordering::Relaxed);
235
236         let t = thread::spawn(move || {
237             // If this syscall runs first, then we'll be woken up by
238             // the main thread's FUTEX_WAKE, and all is fine.
239             //
240             // If this sycall runs after the main thread's store
241             // and FUTEX_WAKE, the syscall must observe that
242             // the FUTEX is FREE != HELD and return without waiting
243             // or we'll deadlock.
244             unsafe {
245                 let ret = libc::syscall(
246                     libc::SYS_futex,
247                     &FUTEX as *const AtomicI32,
248                     libc::FUTEX_WAIT,
249                     HELD,
250                     ptr::null::<libc::timespec>(),
251                 );
252                 if ret == 0 {
253                     // We actually slept. And then woke up again. So we should be ordered-after
254                     // what happened-before the FUTEX_WAKE. So this is not a race.
255                     assert_eq!(DATA, 1);
256                     // Also remember that this happened at least once.
257                     WOKEN.fetch_add(1, Ordering::Relaxed);
258                 }
259             }
260         });
261         // Increase the chance that the other thread actually goes to sleep.
262         // (5 yields in a loop seem to make that happen around 40% of the time.)
263         for _ in 0..5 {
264             thread::yield_now();
265         }
266
267         FUTEX.store(FREE, Ordering::Relaxed);
268         unsafe {
269             DATA = 1;
270             libc::syscall(libc::SYS_futex, &FUTEX as *const AtomicI32, libc::FUTEX_WAKE, 1);
271         }
272
273         t.join().unwrap();
274     }
275
276     // Make sure we got the interesting case (of having woken a thread) at least once, but not *each* time.
277     let woken = WOKEN.load(Ordering::Relaxed);
278     //eprintln!("waking happened {woken} times");
279     assert!(woken > 0 && woken < rounds);
280 }
281
282 fn main() {
283     wake_nobody();
284     wake_dangling();
285     wait_wrong_val();
286     wait_timeout();
287     wait_absolute_timeout();
288     wait_wake();
289     wait_wake_bitset();
290     concurrent_wait_wake();
291 }