Skip to content

Commit fdc4932

Browse files
committed
miri: shim pthread_cond_timedwait_relative_np
1 parent c46fdd3 commit fdc4932

File tree

4 files changed

+64
-7
lines changed

4 files changed

+64
-7
lines changed

src/shims/unix/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
815815
"pthread_cond_timedwait" => {
816816
let [cond, mutex, abstime] =
817817
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
818-
this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
818+
this.pthread_cond_timedwait(cond, mutex, abstime, dest, /* macos_relative_np */ false)?;
819819
}
820820
"pthread_cond_destroy" => {
821821
let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;

src/shims/unix/macos/foreign_items.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
307307
this.os_unfair_lock_assert_not_owner(lock_op)?;
308308
}
309309

310+
"pthread_cond_timedwait_relative_np" => {
311+
let [cond, mutex, reltime] =
312+
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
313+
this.pthread_cond_timedwait(cond, mutex, reltime, dest, /* macos_relative_np */ true)?;
314+
}
315+
310316
_ => return interp_ok(EmulateItemResult::NotSupported),
311317
};
312318

src/shims/unix/sync.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -834,8 +834,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
834834
&mut self,
835835
cond_op: &OpTy<'tcx>,
836836
mutex_op: &OpTy<'tcx>,
837-
abstime_op: &OpTy<'tcx>,
837+
timeout_op: &OpTy<'tcx>,
838838
dest: &MPlaceTy<'tcx>,
839+
macos_relative_np: bool,
839840
) -> InterpResult<'tcx> {
840841
let this = self.eval_context_mut();
841842

@@ -844,7 +845,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
844845

845846
// Extract the timeout.
846847
let duration = match this
847-
.read_timespec(&this.deref_pointer_as(abstime_op, this.libc_ty_layout("timespec"))?)?
848+
.read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)?
848849
{
849850
Some(duration) => duration,
850851
None => {
@@ -853,14 +854,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
853854
return interp_ok(());
854855
}
855856
};
856-
if data.clock == TimeoutClock::RealTime {
857-
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
858-
}
857+
858+
let (clock, anchor) = if macos_relative_np {
859+
// `pthread_cond_timedwait_relative_np` always measures time against the
860+
// monotonic clock, regardless of the condvar clock.
861+
(TimeoutClock::Monotonic, TimeoutAnchor::Relative)
862+
} else {
863+
if data.clock == TimeoutClock::RealTime {
864+
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
865+
}
866+
867+
(data.clock, TimeoutAnchor::Absolute)
868+
};
859869

860870
this.condvar_wait(
861871
data.condvar_ref,
862872
mutex_ref,
863-
Some((data.clock, TimeoutAnchor::Absolute, duration)),
873+
Some((clock, anchor, duration)),
864874
Scalar::from_i32(0),
865875
this.eval_libc("ETIMEDOUT"), // retval_timeout
866876
dest.clone(),
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//@only-target: apple # `pthread_cond_timedwait_relative_np` is a non-standard extension
2+
3+
use std::time::Instant;
4+
5+
// FIXME: remove once this is in libc.
6+
mod libc {
7+
pub use ::libc::*;
8+
unsafe extern "C" {
9+
pub unsafe fn pthread_cond_timedwait_relative_np(
10+
cond: *mut libc::pthread_cond_t,
11+
lock: *mut libc::pthread_mutex_t,
12+
timeout: *const libc::timespec,
13+
) -> libc::c_int;
14+
}
15+
}
16+
17+
fn main() {
18+
unsafe {
19+
let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
20+
let mut cond: libc::pthread_cond_t = libc::PTHREAD_COND_INITIALIZER;
21+
22+
// Wait for 100 ms.
23+
let timeout = libc::timespec { tv_sec: 0, tv_nsec: 100_000_000 };
24+
25+
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
26+
27+
let current_time = Instant::now();
28+
assert_eq!(
29+
libc::pthread_cond_timedwait_relative_np(&mut cond, &mut mutex, &timeout),
30+
libc::ETIMEDOUT
31+
);
32+
let elapsed_time = current_time.elapsed().as_millis();
33+
// This is actually deterministic (since isolation remains enabled),
34+
// but can change slightly with Rust updates.
35+
assert!(90 <= elapsed_time && elapsed_time <= 110);
36+
37+
assert_eq!(libc::pthread_mutex_unlock(&mut mutex), 0);
38+
assert_eq!(libc::pthread_mutex_destroy(&mut mutex), 0);
39+
assert_eq!(libc::pthread_cond_destroy(&mut cond), 0);
40+
}
41+
}

0 commit comments

Comments
 (0)