use std::sync::atomic::{AtomicBool, Ordering};
use rustc_target::abi::Size;
use crate::*;
#[inline]
fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" | "macos" => 0,
os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"),
})
}
fn mutexattr_get_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
mutexattr_kind_offset(ecx)?,
ecx.libc_ty_layout("pthread_mutexattr_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
}
fn mutexattr_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
attr_op,
mutexattr_kind_offset(ecx)?,
Scalar::from_i32(kind),
ecx.libc_ty_layout("pthread_mutexattr_t"),
ecx.machine.layouts.i32,
)
}
const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000;
fn is_mutex_kind_default<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> {
Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"))
}
fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> {
let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL");
Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG))
}
fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"macos" => 4,
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
};
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap();
assert_eq!(
id, 0,
"PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: id is not 0"
);
}
Ok(offset)
}
fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
let kind_field = static_initializer
.offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx)
.unwrap();
let kind = ecx.read_scalar(&kind_field).unwrap().to_i32().unwrap();
assert_eq!(
kind,
ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
"PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
);
}
offset
}
fn mutex_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, MutexId> {
ecx.mutex_get_or_create_id(
mutex_op,
ecx.libc_ty_layout("pthread_mutex_t"),
mutex_id_offset(ecx)?,
)
}
fn mutex_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
mutex_op,
mutex_id_offset(ecx)?,
Scalar::from_u32(0),
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.u32,
)
}
fn mutex_get_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
mutex_op,
mutex_kind_offset(ecx),
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
}
fn mutex_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
mutex_op,
mutex_kind_offset(ecx),
Scalar::from_i32(kind),
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.i32,
)
}
fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"macos" => 4,
os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"),
};
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap();
assert_eq!(
id, 0,
"PTHREAD_RWLOCK_INITIALIZER is incompatible with our pthread_rwlock layout: id is not 0"
);
}
Ok(offset)
}
fn rwlock_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
rwlock_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, RwLockId> {
ecx.rwlock_get_or_create_id(
rwlock_op,
ecx.libc_ty_layout("pthread_rwlock_t"),
rwlock_id_offset(ecx)?,
)
}
#[inline]
fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"),
})
}
fn condattr_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
condattr_clock_offset(ecx)?,
ecx.libc_ty_layout("pthread_condattr_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
}
fn condattr_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
attr_op,
condattr_clock_offset(ecx)?,
Scalar::from_i32(clock_id),
ecx.libc_ty_layout("pthread_condattr_t"),
ecx.machine.layouts.i32,
)
}
fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"macos" => 4,
os => throw_unsup_format!("`pthread_cond` is not supported on {os}"),
};
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap();
assert_eq!(
id, 0,
"PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: id is not 0"
);
}
Ok(offset)
}
fn is_cond_clock_realtime<'tcx>(ecx: &MiriInterpCx<'tcx>, clock_id: i32) -> bool {
clock_id == ecx.eval_libc_i32("CLOCK_REALTIME")
|| (clock_id == 0 && clock_id != ecx.eval_libc_i32("CLOCK_MONOTONIC"))
}
fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
let offset = 8;
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_i32().unwrap();
assert!(
is_cond_clock_realtime(ecx, id),
"PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
);
}
offset
}
fn cond_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, CondvarId> {
ecx.condvar_get_or_create_id(
cond_op,
ecx.libc_ty_layout("pthread_cond_t"),
cond_id_offset(ecx)?,
)
}
fn cond_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
cond_id_offset(ecx)?,
Scalar::from_i32(0),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.u32,
)
}
fn cond_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
cond_op,
cond_clock_offset(ecx),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
}
fn cond_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
cond_clock_offset(ecx),
Scalar::from_i32(clock_id),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.i32,
)
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
mutexattr_set_kind(this, attr_op, default_kind)?;
Ok(())
}
fn pthread_mutexattr_settype(
&mut self,
attr_op: &OpTy<'tcx>,
kind_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let kind = this.read_scalar(kind_op)?.to_i32()?;
if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL") {
let normal_kind = kind | PTHREAD_MUTEX_NORMAL_FLAG;
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"));
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK"));
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE"));
mutexattr_set_kind(this, attr_op, normal_kind)?;
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
{
mutexattr_set_kind(this, attr_op, kind)?;
} else {
let einval = this.eval_libc_i32("EINVAL");
return Ok(Scalar::from_i32(einval));
}
Ok(Scalar::from_i32(0))
}
fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
mutexattr_get_kind(this, attr_op)?;
this.write_uninit(
&this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?,
)?;
Ok(())
}
fn pthread_mutex_init(
&mut self,
mutex_op: &OpTy<'tcx>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let attr = this.read_pointer(attr_op)?;
let kind = if this.ptr_is_null(attr)? {
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
} else {
mutexattr_get_kind(this, attr_op)?
};
mutex_reset_id(this, mutex_op)?;
mutex_set_kind(this, mutex_op, kind)?;
Ok(())
}
fn pthread_mutex_lock(
&mut self,
mutex_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(this, mutex_op)?;
let ret = if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id);
if owner_thread != this.active_thread() {
this.mutex_enqueue_and_block(id, Some((Scalar::from_i32(0), dest.clone())));
return Ok(());
} else {
if is_mutex_kind_default(this, kind)? {
throw_ub_format!("trying to acquire already locked default mutex");
} else if is_mutex_kind_normal(this, kind)? {
throw_machine_stop!(TerminationInfo::Deadlock);
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
this.eval_libc_i32("EDEADLK")
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
this.mutex_lock(id);
0
} else {
throw_unsup_format!(
"called pthread_mutex_lock on an unsupported type of mutex"
);
}
}
} else {
this.mutex_lock(id);
0
};
this.write_scalar(Scalar::from_i32(ret), dest)?;
Ok(())
}
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(this, mutex_op)?;
Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id);
if owner_thread != this.active_thread() {
this.eval_libc_i32("EBUSY")
} else {
if is_mutex_kind_default(this, kind)?
|| is_mutex_kind_normal(this, kind)?
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
{
this.eval_libc_i32("EBUSY")
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
this.mutex_lock(id);
0
} else {
throw_unsup_format!(
"called pthread_mutex_trylock on an unsupported type of mutex"
);
}
}
} else {
this.mutex_lock(id);
0
}))
}
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(this, mutex_op)?;
if let Some(_old_locked_count) = this.mutex_unlock(id)? {
Ok(Scalar::from_i32(0))
} else {
if is_mutex_kind_default(this, kind)? {
throw_ub_format!(
"unlocked a default mutex that was not locked by the current thread"
);
} else if is_mutex_kind_normal(this, kind)? {
throw_ub_format!(
"unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
);
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
{
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM")))
} else {
throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex");
}
}
}
fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let id = mutex_get_id(this, mutex_op)?;
if this.mutex_is_locked(id) {
throw_ub_format!("destroyed a locked mutex");
}
mutex_get_kind(this, mutex_op)?;
mutex_get_id(this, mutex_op)?;
this.write_uninit(
&this.deref_pointer_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?,
)?;
Ok(())
}
fn pthread_rwlock_rdlock(
&mut self,
rwlock_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
if this.rwlock_is_write_locked(id) {
this.rwlock_enqueue_and_block_reader(id, Scalar::from_i32(0), dest.clone());
} else {
this.rwlock_reader_lock(id);
this.write_null(dest)?;
}
Ok(())
}
fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
if this.rwlock_is_write_locked(id) {
Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
} else {
this.rwlock_reader_lock(id);
Ok(Scalar::from_i32(0))
}
}
fn pthread_rwlock_wrlock(
&mut self,
rwlock_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
if this.rwlock_is_locked(id) {
this.rwlock_enqueue_and_block_writer(id, Scalar::from_i32(0), dest.clone());
} else {
this.rwlock_writer_lock(id);
this.write_null(dest)?;
}
Ok(())
}
fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
if this.rwlock_is_locked(id) {
Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
} else {
this.rwlock_writer_lock(id);
Ok(Scalar::from_i32(0))
}
}
fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
#[allow(clippy::if_same_then_else)]
if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? {
Ok(())
} else {
throw_ub_format!("unlocked an rwlock that was not locked by the active thread");
}
}
fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
if this.rwlock_is_locked(id) {
throw_ub_format!("destroyed a locked rwlock");
}
rwlock_get_id(this, rwlock_op)?;
this.write_uninit(
&this.deref_pointer_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?,
)?;
Ok(())
}
fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
if this.tcx.sess.target.os != "macos" {
let default_clock_id = this.eval_libc_i32("CLOCK_REALTIME");
condattr_set_clock_id(this, attr_op, default_clock_id)?;
}
Ok(())
}
fn pthread_condattr_setclock(
&mut self,
attr_op: &OpTy<'tcx>,
clock_id_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
if clock_id == this.eval_libc_i32("CLOCK_REALTIME")
|| clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")
{
condattr_set_clock_id(this, attr_op, clock_id)?;
} else {
let einval = this.eval_libc_i32("EINVAL");
return Ok(Scalar::from_i32(einval));
}
Ok(Scalar::from_i32(0))
}
fn pthread_condattr_getclock(
&mut self,
attr_op: &OpTy<'tcx>,
clk_id_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let clock_id = condattr_get_clock_id(this, attr_op)?;
this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?;
Ok(())
}
fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
if this.tcx.sess.target.os != "macos" {
condattr_get_clock_id(this, attr_op)?;
}
this.write_uninit(
&this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?,
)?;
Ok(())
}
fn pthread_cond_init(
&mut self,
cond_op: &OpTy<'tcx>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let attr = this.read_pointer(attr_op)?;
let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" {
this.eval_libc_i32("CLOCK_REALTIME")
} else {
condattr_get_clock_id(this, attr_op)?
};
cond_reset_id(this, cond_op)?;
cond_set_clock_id(this, cond_op, clock_id)?;
Ok(())
}
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
this.condvar_signal(id)?;
Ok(())
}
fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
while this.condvar_signal(id)? {}
Ok(())
}
fn pthread_cond_wait(
&mut self,
cond_op: &OpTy<'tcx>,
mutex_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
let mutex_id = mutex_get_id(this, mutex_op)?;
this.condvar_wait(
id,
mutex_id,
None, Scalar::from_i32(0),
Scalar::from_i32(0), dest.clone(),
)?;
Ok(())
}
fn pthread_cond_timedwait(
&mut self,
cond_op: &OpTy<'tcx>,
mutex_op: &OpTy<'tcx>,
abstime_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
let mutex_id = mutex_get_id(this, mutex_op)?;
let clock_id = cond_get_clock_id(this, cond_op)?;
let duration = match this
.read_timespec(&this.deref_pointer_as(abstime_op, this.libc_ty_layout("timespec"))?)?
{
Some(duration) => duration,
None => {
let einval = this.eval_libc("EINVAL");
this.write_scalar(einval, dest)?;
return Ok(());
}
};
let timeout_clock = if is_cond_clock_realtime(this, clock_id) {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
TimeoutClock::RealTime
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") {
TimeoutClock::Monotonic
} else {
throw_unsup_format!("unsupported clock id: {}", clock_id);
};
this.condvar_wait(
id,
mutex_id,
Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
Scalar::from_i32(0),
this.eval_libc("ETIMEDOUT"), dest.clone(),
)?;
Ok(())
}
fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
if this.condvar_is_awaited(id) {
throw_ub_format!("destroying an awaited conditional variable");
}
cond_get_id(this, cond_op)?;
cond_get_clock_id(this, cond_op)?;
this.write_uninit(&this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?;
Ok(())
}
}