Skip to content

Commit 406ab4c

Browse files
fix and update tests
1 parent ce92c0a commit 406ab4c

File tree

1 file changed

+136
-48
lines changed

1 file changed

+136
-48
lines changed

tests/pass-dep/concurrency/freebsd-futex.rs

Lines changed: 136 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,53 @@
11
//@only-target: freebsd
2-
//@compile-flags: -Zmiri-preemption-rate=0
2+
//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-isolation
33

4-
use std::mem::MaybeUninit;
4+
use std::mem::{self, MaybeUninit};
55
use std::ptr::{self, addr_of};
66
use std::sync::atomic::AtomicU32;
77
use std::time::{Duration, Instant};
88
use std::{io, thread};
99

1010
fn wake_nobody() {
11-
// TODO: _umtx_op does not return how many threads were woken up
12-
// How do i test this?
11+
// Current thread waits on futex
12+
// New thread wakes up 0 threads waiting on that futex
13+
// Current thread should time out
14+
static mut FUTEX: u32 = 0;
15+
16+
let waker = thread::spawn(|| {
17+
thread::sleep(Duration::from_millis(200));
18+
19+
unsafe {
20+
assert_eq!(
21+
libc::_umtx_op(
22+
addr_of!(FUTEX) as *mut _,
23+
libc::UMTX_OP_WAKE_PRIVATE,
24+
0, // wake up 0 waiters
25+
ptr::null_mut::<libc::c_void>(),
26+
ptr::null_mut::<libc::c_void>(),
27+
),
28+
0
29+
);
30+
}
31+
});
32+
let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 400_000_000 };
33+
let timeout_size_arg =
34+
ptr::without_provenance_mut::<libc::c_void>(mem::size_of::<libc::timespec>());
35+
unsafe {
36+
assert_eq!(
37+
libc::_umtx_op(
38+
addr_of!(FUTEX) as *mut _,
39+
libc::UMTX_OP_WAIT_UINT_PRIVATE,
40+
0,
41+
timeout_size_arg,
42+
&mut timeout as *mut _ as _,
43+
),
44+
-1
45+
);
46+
// main thread did not get woken up, so it timed out
47+
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
48+
}
49+
50+
waker.join().unwrap();
1351
}
1452

1553
fn wake_dangling() {
@@ -36,6 +74,7 @@ fn wake_dangling() {
3674
fn wait_wrong_val() {
3775
let futex: u32 = 123;
3876

77+
// Wait with a wrong value just returns 0
3978
unsafe {
4079
assert_eq!(
4180
libc::_umtx_op(
@@ -45,35 +84,69 @@ fn wait_wrong_val() {
4584
ptr::null_mut::<libc::c_void>(),
4685
ptr::null_mut::<libc::c_void>(),
4786
),
48-
-1
87+
0
4988
);
50-
// man page doesn't document but we set EINVAL for consistency?
51-
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
5289
}
5390
}
5491

5592
fn wait_relative_timeout() {
56-
let start = Instant::now();
93+
fn without_timespec() {
94+
let start = Instant::now();
5795

58-
let futex: u32 = 123;
96+
let futex: u32 = 123;
5997

60-
// Wait for 200ms, with nobody waking us up early
61-
unsafe {
62-
assert_eq!(
63-
libc::_umtx_op(
64-
ptr::from_ref(&futex).cast_mut().cast(),
65-
libc::UMTX_OP_WAIT_UINT_PRIVATE,
66-
123,
67-
&mut libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 } as *mut _ as *mut _,
68-
ptr::null_mut::<libc::c_void>(),
69-
),
70-
-1
71-
);
72-
// man page doesn't document but we set EINVAL for consistency
73-
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
98+
let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 };
99+
let timeout_size_arg =
100+
ptr::without_provenance_mut::<libc::c_void>(mem::size_of::<libc::timespec>());
101+
// Wait for 200ms, with nobody waking us up early
102+
unsafe {
103+
assert_eq!(
104+
libc::_umtx_op(
105+
ptr::from_ref(&futex).cast_mut().cast(),
106+
libc::UMTX_OP_WAIT_UINT_PRIVATE,
107+
123,
108+
timeout_size_arg,
109+
&mut timeout as *mut _ as _,
110+
),
111+
-1
112+
);
113+
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
114+
}
115+
116+
assert!((200..1000).contains(&start.elapsed().as_millis()));
74117
}
75118

76-
assert!((200..1000).contains(&start.elapsed().as_millis()));
119+
fn with_timespec() {
120+
let futex: u32 = 123;
121+
let mut timeout = libc::_umtx_time {
122+
_timeout: libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 },
123+
_flags: 0,
124+
_clockid: libc::CLOCK_MONOTONIC as u32,
125+
};
126+
let timeout_size_arg =
127+
ptr::without_provenance_mut::<libc::c_void>(mem::size_of::<libc::_umtx_time>());
128+
129+
let start = Instant::now();
130+
131+
// Wait for 200ms, with nobody waking us up early
132+
unsafe {
133+
assert_eq!(
134+
libc::_umtx_op(
135+
ptr::from_ref(&futex).cast_mut().cast(),
136+
libc::UMTX_OP_WAIT_UINT_PRIVATE,
137+
123,
138+
timeout_size_arg,
139+
&mut timeout as *mut _ as _,
140+
),
141+
-1
142+
);
143+
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
144+
}
145+
assert!((200..1000).contains(&start.elapsed().as_millis()));
146+
}
147+
148+
without_timespec();
149+
with_timespec();
77150
}
78151

79152
fn wait_absolute_timeout() {
@@ -93,14 +166,14 @@ fn wait_absolute_timeout() {
93166
timeout.tv_sec += 1;
94167
}
95168

96-
// Create umtx_timeout struct
169+
// Create umtx_timeout struct with that absolute timeout.
97170
let umtx_timeout = libc::_umtx_time {
98171
_timeout: timeout,
99172
_flags: libc::UMTX_ABSTIME,
100173
_clockid: libc::CLOCK_MONOTONIC as u32,
101174
};
102175
let umtx_timeout_ptr = &umtx_timeout as *const _;
103-
let umtx_timeout_size = std::mem::size_of_val(&umtx_timeout);
176+
let umtx_timeout_size = ptr::without_provenance_mut(mem::size_of_val(&umtx_timeout));
104177

105178
let futex: u32 = 123;
106179

@@ -111,55 +184,70 @@ fn wait_absolute_timeout() {
111184
ptr::from_ref(&futex).cast_mut().cast(),
112185
libc::UMTX_OP_WAIT_UINT_PRIVATE,
113186
123,
114-
ptr::without_provenance_mut(umtx_timeout_size),
187+
umtx_timeout_size,
115188
umtx_timeout_ptr as *mut _,
116189
),
117190
-1
118191
);
119-
// man page doesn't document but we set EINVAL for consistency
120192
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
121193
}
122194
assert!((200..1000).contains(&start.elapsed().as_millis()));
123195
}
124196

125197
fn wait_wake() {
126-
let start = Instant::now();
127-
128198
static mut FUTEX: u32 = 0;
129199

130-
let t = thread::spawn(move || {
131-
thread::sleep(Duration::from_millis(200));
200+
let t1 = thread::spawn(move || {
201+
let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 500_000_000 };
202+
let timeout_size_arg =
203+
ptr::without_provenance_mut::<libc::c_void>(mem::size_of::<libc::timespec>());
132204
unsafe {
133-
assert_eq!(
134-
libc::_umtx_op(
135-
addr_of!(FUTEX) as *mut _,
136-
libc::UMTX_OP_WAKE_PRIVATE,
137-
10, // Wake up 10 threads, but we can't check that we woken up 1.
138-
ptr::null_mut::<libc::c_void>(),
139-
ptr::null_mut::<libc::c_void>(),
140-
),
141-
0
205+
libc::_umtx_op(
206+
addr_of!(FUTEX) as *mut _,
207+
libc::UMTX_OP_WAIT_UINT_PRIVATE,
208+
0, // FUTEX is 0
209+
timeout_size_arg,
210+
&mut timeout as *mut _ as _,
211+
);
212+
io::Error::last_os_error().raw_os_error().unwrap() == libc::ETIMEDOUT
213+
}
214+
});
215+
let t2 = thread::spawn(move || {
216+
let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 500_000_000 };
217+
let timeout_size_arg =
218+
ptr::without_provenance_mut::<libc::c_void>(mem::size_of::<libc::timespec>());
219+
unsafe {
220+
libc::_umtx_op(
221+
addr_of!(FUTEX) as *mut _,
222+
libc::UMTX_OP_WAIT_UINT_PRIVATE,
223+
0, // FUTEX is 0
224+
// make sure the threads still exit
225+
timeout_size_arg,
226+
&mut timeout as *mut _ as _,
142227
);
228+
io::Error::last_os_error().raw_os_error().unwrap() == libc::ETIMEDOUT
143229
}
144230
});
145231

232+
// Wake up 1 thread and make sure the other is still waiting
233+
thread::sleep(Duration::from_millis(200));
146234
unsafe {
147235
assert_eq!(
148236
libc::_umtx_op(
149237
addr_of!(FUTEX) as *mut _,
150-
libc::UMTX_OP_WAIT_UINT_PRIVATE,
151-
0, // FUTEX is 0
238+
libc::UMTX_OP_WAKE_PRIVATE,
239+
1,
152240
ptr::null_mut::<libc::c_void>(),
153241
ptr::null_mut::<libc::c_void>(),
154242
),
155243
0
156244
);
157245
}
158-
159-
// When running this in stress-gc mode, things can take quite long.
160-
// So the timeout is 3000 ms.
161-
assert!((200..3000).contains(&start.elapsed().as_millis()));
162-
t.join().unwrap();
246+
// Wait a bit more for good measure.
247+
thread::sleep(Duration::from_millis(100));
248+
let t1_woke_up = t1.join().unwrap();
249+
let t2_woke_up = t2.join().unwrap();
250+
assert!(!(t1_woke_up && t2_woke_up), "Expected only 1 thread to wake up");
163251
}
164252

165253
fn main() {

0 commit comments

Comments
 (0)