2 //@compile-flags: -Zmiri-disable-isolation
4 use std::mem::MaybeUninit;
6 use std::sync::atomic::AtomicI32;
7 use std::sync::atomic::Ordering;
9 use std::time::{Duration, Instant};
14 // Wake 1 waiter. Expect zero waiters woken up, as nobody is waiting.
16 assert_eq!(libc::syscall(libc::SYS_futex, &futex as *const i32, libc::FUTEX_WAKE, 1), 0);
19 // Same, but without omitting the unused arguments.
27 ptr::null::<libc::timespec>(),
37 let futex = Box::new(0);
38 let ptr: *const i32 = &*futex;
41 // Wake 1 waiter. Expect zero waiters woken up, as nobody is waiting.
43 assert_eq!(libc::syscall(libc::SYS_futex, ptr, libc::FUTEX_WAKE, 1), 0);
50 // Only wait if the futex value is 456.
58 ptr::null::<libc::timespec>(),
62 assert_eq!(*libc::__errno_location(), libc::EAGAIN);
67 let start = Instant::now();
71 // Wait for 200ms, with nobody waking us up early.
79 &libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 },
83 assert_eq!(*libc::__errno_location(), libc::ETIMEDOUT);
86 assert!((200..1000).contains(&start.elapsed().as_millis()));
89 fn wait_absolute_timeout() {
90 let start = Instant::now();
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);
100 timeout.tv_nsec += 200_000_000;
101 if timeout.tv_nsec > 1_000_000_000 {
102 timeout.tv_nsec -= 1_000_000_000;
106 let futex: i32 = 123;
108 // Wait for 200ms from now, with nobody waking us up early.
113 &futex as *const i32,
114 libc::FUTEX_WAIT_BITSET,
122 assert_eq!(*libc::__errno_location(), libc::ETIMEDOUT);
125 assert!((200..1000).contains(&start.elapsed().as_millis()));
129 let start = Instant::now();
131 static mut FUTEX: i32 = 0;
133 let t = thread::spawn(move || {
134 thread::sleep(Duration::from_millis(200));
139 &FUTEX as *const i32,
141 10, // Wake up at most 10 threads.
143 1, // Woken up one thread.
152 &FUTEX as *const i32,
155 ptr::null::<libc::timespec>(),
161 assert!((200..1000).contains(&start.elapsed().as_millis()));
165 fn wait_wake_bitset() {
166 let start = Instant::now();
168 static mut FUTEX: i32 = 0;
170 let t = thread::spawn(move || {
171 thread::sleep(Duration::from_millis(200));
176 &FUTEX as *const i32,
177 libc::FUTEX_WAKE_BITSET,
178 10, // Wake up at most 10 threads.
179 ptr::null::<libc::timespec>(),
183 0, // Didn't match any thread.
186 thread::sleep(Duration::from_millis(200));
191 &FUTEX as *const i32,
192 libc::FUTEX_WAKE_BITSET,
193 10, // Wake up at most 10 threads.
194 ptr::null::<libc::timespec>(),
198 1, // Woken up one thread.
207 &FUTEX as *const i32,
208 libc::FUTEX_WAIT_BITSET,
210 ptr::null::<libc::timespec>(),
218 assert!((400..1000).contains(&start.elapsed().as_millis()));
222 fn concurrent_wait_wake() {
226 static FUTEX: AtomicI32 = AtomicI32::new(0);
227 static mut DATA: i32 = 0;
228 static WOKEN: AtomicI32 = AtomicI32::new(0);
232 unsafe { DATA = 0 }; // Reset
233 // Suppose the main thread is holding a lock implemented using futex...
234 FUTEX.store(HELD, Ordering::Relaxed);
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.
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.
245 let ret = libc::syscall(
247 &FUTEX as *const AtomicI32,
250 ptr::null::<libc::timespec>(),
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.
256 // Also remember that this happened at least once.
257 WOKEN.fetch_add(1, Ordering::Relaxed);
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.)
267 FUTEX.store(FREE, Ordering::Relaxed);
270 libc::syscall(libc::SYS_futex, &FUTEX as *const AtomicI32, libc::FUTEX_WAKE, 1);
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);
287 wait_absolute_timeout();
290 concurrent_wait_wake();