Skip to content

Commit c8a5d4b

Browse files
committed
relative futex and condvar timeouts can work with isolation
1 parent a0fbf0d commit c8a5d4b

File tree

5 files changed

+100
-7
lines changed

5 files changed

+100
-7
lines changed

src/tools/miri/src/shims/unix/linux/sync.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ pub fn futex<'tcx>(
9090
let timeout_time = if this.ptr_is_null(timeout.ptr)? {
9191
None
9292
} else {
93-
this.check_no_isolation(
94-
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout",
95-
)?;
93+
if op & futex_realtime != 0 {
94+
this.check_no_isolation(
95+
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
96+
)?;
97+
}
9698
let duration = match this.read_timespec(&timeout)? {
9799
Some(duration) => duration,
98100
None => {

src/tools/miri/src/shims/unix/sync.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
743743
) -> InterpResult<'tcx> {
744744
let this = self.eval_context_mut();
745745

746-
this.check_no_isolation("`pthread_cond_timedwait`")?;
747-
748746
let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
749747
let mutex_id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
750748
let active_thread = this.get_active_thread();
@@ -761,6 +759,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
761759
};
762760

763761
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
762+
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
764763
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
765764
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
766765
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())

src/tools/miri/src/shims/windows/sync.rs

-2
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
254254
let timeout_time = if timeout_ms == this.eval_windows("c", "INFINITE")?.to_u32()? {
255255
None
256256
} else {
257-
this.check_no_isolation("`WaitOnAddress` with non-infinite timeout")?;
258-
259257
let duration = Duration::from_millis(timeout_ms.into());
260258
Some(Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()))
261259
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//@ignore-target-windows: No libc on Windows
2+
//@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS.
3+
4+
/// Test that conditional variable timeouts are working properly
5+
/// with monotonic clocks even under isolation.
6+
use std::mem::MaybeUninit;
7+
use std::time::Instant;
8+
9+
fn test_timed_wait_timeout(clock_id: i32) {
10+
unsafe {
11+
let mut attr: MaybeUninit<libc::pthread_condattr_t> = MaybeUninit::uninit();
12+
assert_eq!(libc::pthread_condattr_init(attr.as_mut_ptr()), 0);
13+
assert_eq!(libc::pthread_condattr_setclock(attr.as_mut_ptr(), clock_id), 0);
14+
15+
let mut cond: MaybeUninit<libc::pthread_cond_t> = MaybeUninit::uninit();
16+
assert_eq!(libc::pthread_cond_init(cond.as_mut_ptr(), attr.as_ptr()), 0);
17+
assert_eq!(libc::pthread_condattr_destroy(attr.as_mut_ptr()), 0);
18+
19+
let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
20+
21+
let mut now_mu: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
22+
assert_eq!(libc::clock_gettime(clock_id, now_mu.as_mut_ptr()), 0);
23+
let now = now_mu.assume_init();
24+
// Waiting for a second... mostly because waiting less requires mich more tricky arithmetic.
25+
// FIXME: wait less.
26+
let timeout = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: now.tv_nsec };
27+
28+
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
29+
let current_time = Instant::now();
30+
assert_eq!(
31+
libc::pthread_cond_timedwait(cond.as_mut_ptr(), &mut mutex as *mut _, &timeout),
32+
libc::ETIMEDOUT
33+
);
34+
let elapsed_time = current_time.elapsed().as_millis();
35+
assert!(900 <= elapsed_time && elapsed_time <= 1300);
36+
37+
// Test calling `pthread_cond_timedwait` again with an already elapsed timeout.
38+
assert_eq!(
39+
libc::pthread_cond_timedwait(cond.as_mut_ptr(), &mut mutex as *mut _, &timeout),
40+
libc::ETIMEDOUT
41+
);
42+
43+
// Test that invalid nanosecond values (above 10^9 or negative) are rejected with the
44+
// correct error code.
45+
let invalid_timeout_1 = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: 1_000_000_000 };
46+
assert_eq!(
47+
libc::pthread_cond_timedwait(
48+
cond.as_mut_ptr(),
49+
&mut mutex as *mut _,
50+
&invalid_timeout_1
51+
),
52+
libc::EINVAL
53+
);
54+
let invalid_timeout_2 = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: -1 };
55+
assert_eq!(
56+
libc::pthread_cond_timedwait(
57+
cond.as_mut_ptr(),
58+
&mut mutex as *mut _,
59+
&invalid_timeout_2
60+
),
61+
libc::EINVAL
62+
);
63+
// Test that invalid second values (negative) are rejected with the correct error code.
64+
let invalid_timeout_3 = libc::timespec { tv_sec: -1, tv_nsec: 0 };
65+
assert_eq!(
66+
libc::pthread_cond_timedwait(
67+
cond.as_mut_ptr(),
68+
&mut mutex as *mut _,
69+
&invalid_timeout_3
70+
),
71+
libc::EINVAL
72+
);
73+
74+
assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
75+
assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
76+
assert_eq!(libc::pthread_cond_destroy(cond.as_mut_ptr()), 0);
77+
}
78+
}
79+
80+
fn main() {
81+
test_timed_wait_timeout(libc::CLOCK_MONOTONIC);
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ignore-target-apple: park_timeout on macOS uses the system clock
2+
use std::thread;
3+
use std::time::{Duration, Instant};
4+
5+
fn main() {
6+
let start = Instant::now();
7+
8+
thread::park_timeout(Duration::from_millis(200));
9+
10+
// Thanks to deterministic execution, this will wiat *exactly* 200ms (rounded to 1ms).
11+
assert!((200..201).contains(&start.elapsed().as_millis()));
12+
}

0 commit comments

Comments
 (0)