From: David Cook Date: Tue, 18 Feb 2020 03:30:24 +0000 (-0600) Subject: Implement mutex and rwlock functions X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=dd9896b0f8752c71c82c5d538ec0b115ffb5cf4e;p=rust.git Implement mutex and rwlock functions --- diff --git a/src/lib.rs b/src/lib.rs index c04fbfeab98..2f381b4a345 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt; pub use crate::shims::os_str::EvalContextExt as OsStrEvalContextExt; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as PanicEvalContextExt}; +pub use crate::shims::sync::{EvalContextExt as SyncEvalContextExt}; pub use crate::shims::time::EvalContextExt as TimeEvalContextExt; pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData}; pub use crate::shims::EvalContextExt as ShimsEvalContextExt; diff --git a/src/shims/foreign_items/posix.rs b/src/shims/foreign_items/posix.rs index 061ae93d8f6..2b9e94ba11d 100644 --- a/src/shims/foreign_items/posix.rs +++ b/src/shims/foreign_items/posix.rs @@ -255,28 +255,12 @@ fn emulate_foreign_item_by_name( this.write_null(dest)?; } - // Incomplete shims that we "stub out" just to get pre-main initialziation code to work. + // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. | "pthread_attr_init" | "pthread_attr_destroy" | "pthread_self" - | "pthread_attr_setstacksize" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - this.write_null(dest)?; - } - | "pthread_mutexattr_init" - | "pthread_mutexattr_settype" - | "pthread_mutex_init" - | "pthread_mutexattr_destroy" - | "pthread_mutex_lock" - | "pthread_mutex_trylock" - | "pthread_mutex_unlock" - | "pthread_mutex_destroy" - | "pthread_rwlock_rdlock" - | "pthread_rwlock_tryrdlock" - | "pthread_rwlock_unlock" - | "pthread_rwlock_wrlock" - | "pthread_rwlock_trywrlock" - | "pthread_rwlock_destroy" + | "pthread_attr_setstacksize" | "pthread_condattr_init" | "pthread_condattr_setclock" | "pthread_cond_init" @@ -285,6 +269,77 @@ fn emulate_foreign_item_by_name( => { this.write_null(dest)?; } + + "pthread_mutexattr_init" => { + let result = this.pthread_mutexattr_init(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutexattr_settype" => { + let result = this.pthread_mutexattr_settype(args[0], args[1])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutexattr_destroy" => { + let result = this.pthread_mutexattr_destroy(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutex_init" => { + let result = this.pthread_mutex_init(args[0], args[1])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutex_lock" => { + let result = this.pthread_mutex_lock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutex_trylock" => { + let result = this.pthread_mutex_trylock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutex_unlock" => { + let result = this.pthread_mutex_unlock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_mutex_destroy" => { + let result = this.pthread_mutex_destroy(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_rwlock_rdlock" => { + let result = this.pthread_rwlock_rdlock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_rwlock_tryrdlock" => { + let result = this.pthread_rwlock_tryrdlock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_rwlock_wrlock" => { + let result = this.pthread_rwlock_wrlock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_rwlock_trywrlock" => { + let result = this.pthread_rwlock_trywrlock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_rwlock_unlock" => { + let result = this.pthread_rwlock_unlock(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "pthread_rwlock_destroy" => { + let result = this.pthread_rwlock_destroy(args[0])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + | "signal" | "sigaction" | "sigaltstack" diff --git a/src/shims/mod.rs b/src/shims/mod.rs index e5db537cff1..764e404141e 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -5,6 +5,7 @@ pub mod intrinsics; pub mod os_str; pub mod panic; +pub mod sync; pub mod time; pub mod tls; diff --git a/src/shims/sync.rs b/src/shims/sync.rs new file mode 100644 index 00000000000..c9329586627 --- /dev/null +++ b/src/shims/sync.rs @@ -0,0 +1,436 @@ +use rustc_middle::ty::{TyKind, TypeAndMut}; +use rustc_target::abi::{LayoutOf, Size}; + +use crate::stacked_borrows::Tag; +use crate::*; + +impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + // pthread_mutexattr_t is either 4 or 8 bytes, depending on the platform + // memory layout: store an i32 in the first four bytes equal to the + // corresponding libc mutex kind constant (i.e. PTHREAD_MUTEX_NORMAL) + + fn pthread_mutexattr_init(&mut self, attr_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, attr_op, 4)?; + + let attr = this.read_scalar(attr_op)?.not_undef()?; + if this.is_null(attr)? { + return this.eval_libc_i32("EINVAL"); + } + + let attr_place = this.deref_operand(attr_op)?; + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = attr_place.offset(Size::ZERO, MemPlaceMeta::None, i32_layout, this)?; + let default_kind = this.eval_libc("PTHREAD_MUTEX_DEFAULT")?; + this.write_scalar(default_kind, kind_place.into())?; + + Ok(0) + } + + fn pthread_mutexattr_settype( + &mut self, + attr_op: OpTy<'tcx, Tag>, + kind_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, attr_op, 4)?; + + let attr = this.read_scalar(attr_op)?.not_undef()?; + if this.is_null(attr)? { + return this.eval_libc_i32("EINVAL"); + } + + let kind = this.read_scalar(kind_op)?.not_undef()?; + if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? || + kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? || + kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? { + let attr_place = this.deref_operand(attr_op)?; + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = attr_place.offset(Size::ZERO, MemPlaceMeta::None, i32_layout, this)?; + this.write_scalar(kind, kind_place.into())?; + } else { + let einval = this.eval_libc_i32("EINVAL")?; + return Ok(einval); + } + + Ok(0) + } + + fn pthread_mutexattr_destroy(&mut self, attr_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, attr_op, 4)?; + + let attr = this.read_scalar(attr_op)?.not_undef()?; + if this.is_null(attr)? { + return this.eval_libc_i32("EINVAL"); + } + + let attr_place = this.deref_operand(attr_op)?; + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = attr_place.offset(Size::ZERO, MemPlaceMeta::None, i32_layout, this)?; + this.write_scalar(ScalarMaybeUndef::Undef, kind_place.into())?; + + Ok(0) + } + + // pthread_mutex_t is between 24 and 48 bytes, depending on the platform + // memory layout: + // bytes 0-3: count of how many times this mutex has been locked, as a u32 + // bytes 12-15: mutex kind, as an i32 + // (the kind should be at this offset for compatibility with the static + // initializer macro) + + fn pthread_mutex_init( + &mut self, + mutex_op: OpTy<'tcx, Tag>, + attr_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, mutex_op, 16)?; + check_ptr_target_min_size(this, attr_op, 4)?; + + let mutex = this.read_scalar(mutex_op)?.not_undef()?; + if this.is_null(mutex)? { + return this.eval_libc_i32("EINVAL"); + } + let mutex_place = this.deref_operand(mutex_op)?; + + let i32_layout = this.layout_of(this.tcx.types.i32)?; + + let attr = this.read_scalar(attr_op)?.not_undef()?; + let kind = if this.is_null(attr)? { + this.eval_libc("PTHREAD_MUTEX_DEFAULT")? + } else { + let attr_place = this.deref_operand(attr_op)?; + let attr_kind_place = attr_place.offset(Size::ZERO, MemPlaceMeta::None, i32_layout, this)?; + this.read_scalar(attr_kind_place.into())?.not_undef()? + }; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let locked_count_place = mutex_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + this.write_scalar(Scalar::from_u32(0), locked_count_place.into())?; + + let mutex_kind_place = mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, &*this.tcx)?; + this.write_scalar(kind, mutex_kind_place.into())?; + + Ok(0) + } + + fn pthread_mutex_lock(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, mutex_op, 16)?; + + let mutex = this.read_scalar(mutex_op)?.not_undef()?; + if this.is_null(mutex)? { + return this.eval_libc_i32("EINVAL"); + } + let mutex_place = this.deref_operand(mutex_op)?; + + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, this)?; + let kind = this.read_scalar(kind_place.into())?.not_undef()?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let locked_count_place = mutex_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let locked_count = this.read_scalar(locked_count_place.into())?.to_u32()?; + + if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? { + if locked_count == 0 { + this.write_scalar(Scalar::from_u32(1), locked_count_place.into())?; + Ok(0) + } else { + throw_unsup_format!("Deadlock due to locking a PTHREAD_MUTEX_NORMAL mutex twice"); + } + } else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? { + if locked_count == 0 { + this.write_scalar(Scalar::from_u32(1), locked_count_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EDEADLK") + } + } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? { + this.write_scalar(Scalar::from_u32(locked_count + 1), locked_count_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EINVAL") + } + } + + fn pthread_mutex_trylock(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, mutex_op, 16)?; + + let mutex = this.read_scalar(mutex_op)?.not_undef()?; + if this.is_null(mutex)? { + return this.eval_libc_i32("EINVAL"); + } + let mutex_place = this.deref_operand(mutex_op)?; + + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, this)?; + let kind = this.read_scalar(kind_place.into())?.not_undef()?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let locked_count_place = mutex_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let locked_count = this.read_scalar(locked_count_place.into())?.to_u32()?; + + if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? || + kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? { + if locked_count == 0 { + this.write_scalar(Scalar::from_u32(1), locked_count_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EBUSY") + } + } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? { + this.write_scalar(Scalar::from_u32(locked_count + 1), locked_count_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EINVAL") + } + } + + fn pthread_mutex_unlock(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, mutex_op, 16)?; + + let mutex = this.read_scalar(mutex_op)?.not_undef()?; + if this.is_null(mutex)? { + return this.eval_libc_i32("EINVAL"); + } + let mutex_place = this.deref_operand(mutex_op)?; + + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, this)?; + let kind = this.read_scalar(kind_place.into())?.not_undef()?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let locked_count_place = mutex_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let locked_count = this.read_scalar(locked_count_place.into())?.to_u32()?; + + if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? { + if locked_count == 1 { + this.write_scalar(Scalar::from_u32(0), locked_count_place.into())?; + Ok(0) + } else { + throw_ub_format!("Attempted to unlock a PTHREAD_MUTEX_NORMAL mutex that was not locked"); + } + } else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? { + if locked_count == 1 { + this.write_scalar(Scalar::from_u32(0), locked_count_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EPERM") + } + } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? { + if locked_count > 0 { + this.write_scalar(Scalar::from_u32(locked_count - 1), locked_count_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EPERM") + } + } else { + this.eval_libc_i32("EINVAL") + } + } + + fn pthread_mutex_destroy(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, mutex_op, 16)?; + + let mutex = this.read_scalar(mutex_op)?.not_undef()?; + if this.is_null(mutex)? { + return this.eval_libc_i32("EINVAL"); + } + let mutex_place = this.deref_operand(mutex_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let locked_count_place = mutex_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + if this.read_scalar(locked_count_place.into())?.to_u32()? != 0 { + return this.eval_libc_i32("EBUSY"); + } + + let i32_layout = this.layout_of(this.tcx.types.i32)?; + let kind_place = mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, this)?; + this.write_scalar(ScalarMaybeUndef::Undef, kind_place.into())?; + this.write_scalar(ScalarMaybeUndef::Undef, locked_count_place.into())?; + + Ok(0) + } + + // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform + // memory layout: + // bytes 0-3: reader count, as a u32 + // bytes 4-7: writer count, as a u32 + + fn pthread_rwlock_rdlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, rwlock_op, 8)?; + + let rwlock = this.read_scalar(rwlock_op)?.not_undef()?; + if this.is_null(rwlock)? { + return this.eval_libc_i32("EINVAL"); + } + let rwlock_place = this.deref_operand(rwlock_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let readers_place = rwlock_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let writers_place = rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, u32_layout, this)?; + let readers = this.read_scalar(readers_place.into())?.to_u32()?; + let writers = this.read_scalar(writers_place.into())?.to_u32()?; + if writers != 0 { + throw_unsup_format!("Deadlock due to read-locking a pthreads read-write lock while it is already write-locked"); + } else { + this.write_scalar(Scalar::from_u32(readers + 1), readers_place.into())?; + Ok(0) + } + } + + fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, rwlock_op, 8)?; + + let rwlock = this.read_scalar(rwlock_op)?.not_undef()?; + if this.is_null(rwlock)? { + return this.eval_libc_i32("EINVAL"); + } + let rwlock_place = this.deref_operand(rwlock_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let readers_place = rwlock_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let writers_place = rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, u32_layout, this)?; + let readers = this.read_scalar(readers_place.into())?.to_u32()?; + let writers = this.read_scalar(writers_place.into())?.to_u32()?; + if writers != 0 { + this.eval_libc_i32("EBUSY") + } else { + this.write_scalar(Scalar::from_u32(readers + 1), readers_place.into())?; + Ok(0) + } + } + + fn pthread_rwlock_wrlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, rwlock_op, 8)?; + + let rwlock = this.read_scalar(rwlock_op)?.not_undef()?; + if this.is_null(rwlock)? { + return this.eval_libc_i32("EINVAL"); + } + let rwlock_place = this.deref_operand(rwlock_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let readers_place = rwlock_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let writers_place = rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, u32_layout, this)?; + let readers = this.read_scalar(readers_place.into())?.to_u32()?; + let writers = this.read_scalar(writers_place.into())?.to_u32()?; + if readers != 0 { + throw_unsup_format!("Deadlock due to write-locking a pthreads read-write lock while it is already read-locked"); + } else if writers != 0 { + throw_unsup_format!("Deadlock due to write-locking a pthreads read-write lock while it is already write-locked"); + } else { + this.write_scalar(Scalar::from_u32(1), writers_place.into())?; + Ok(0) + } + } + + fn pthread_rwlock_trywrlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, rwlock_op, 8)?; + + let rwlock = this.read_scalar(rwlock_op)?.not_undef()?; + if this.is_null(rwlock)? { + return this.eval_libc_i32("EINVAL"); + } + let rwlock_place = this.deref_operand(rwlock_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let readers_place = rwlock_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let writers_place = rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, u32_layout, this)?; + let readers = this.read_scalar(readers_place.into())?.to_u32()?; + let writers = this.read_scalar(writers_place.into())?.to_u32()?; + if readers != 0 || writers != 0 { + this.eval_libc_i32("EBUSY") + } else { + this.write_scalar(Scalar::from_u32(1), writers_place.into())?; + Ok(0) + } + } + + fn pthread_rwlock_unlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, rwlock_op, 8)?; + + let rwlock = this.read_scalar(rwlock_op)?.not_undef()?; + if this.is_null(rwlock)? { + return this.eval_libc_i32("EINVAL"); + } + let rwlock_place = this.deref_operand(rwlock_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let readers_place = rwlock_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + let writers_place = rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, u32_layout, this)?; + let readers = this.read_scalar(readers_place.into())?.to_u32()?; + let writers = this.read_scalar(writers_place.into())?.to_u32()?; + if readers != 0 { + this.write_scalar(Scalar::from_u32(readers - 1), readers_place.into())?; + Ok(0) + } else if writers != 0 { + this.write_scalar(Scalar::from_u32(0), writers_place.into())?; + Ok(0) + } else { + this.eval_libc_i32("EPERM") + } + } + + fn pthread_rwlock_destroy(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + check_ptr_target_min_size(this, rwlock_op, 8)?; + + let rwlock = this.read_scalar(rwlock_op)?.not_undef()?; + if this.is_null(rwlock)? { + return this.eval_libc_i32("EINVAL"); + } + let rwlock_place = this.deref_operand(rwlock_op)?; + + let u32_layout = this.layout_of(this.tcx.types.u32)?; + let readers_place = rwlock_place.offset(Size::ZERO, MemPlaceMeta::None, u32_layout, this)?; + if this.read_scalar(readers_place.into())?.to_u32()? != 0 { + return this.eval_libc_i32("EBUSY"); + } + let writers_place = rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, u32_layout, this)?; + if this.read_scalar(writers_place.into())?.to_u32()? != 0 { + return this.eval_libc_i32("EBUSY"); + } + + this.write_scalar(ScalarMaybeUndef::Undef, readers_place.into())?; + this.write_scalar(ScalarMaybeUndef::Undef, writers_place.into())?; + + Ok(0) + } +} + +fn check_ptr_target_min_size<'mir, 'tcx: 'mir>(ecx: &MiriEvalContext<'mir, 'tcx>, operand: OpTy<'tcx, Tag>, min_size: u64) -> InterpResult<'tcx, ()> { + let target_ty = match operand.layout.ty.kind { + TyKind::RawPtr(TypeAndMut{ ty, mutbl: _ }) => ty, + _ => panic!("Argument to pthread function was not a raw pointer"), + }; + let target_layout = ecx.layout_of(target_ty)?; + assert!(target_layout.size.bytes() >= min_size); + Ok(()) +} diff --git a/tests/run-pass/sync.rs b/tests/run-pass/sync.rs index 8c92b47fb27..d6ce939c6c3 100644 --- a/tests/run-pass/sync.rs +++ b/tests/run-pass/sync.rs @@ -1,4 +1,3 @@ -// Just instantiate some data structures to make sure we got all their foreign items covered. // Requires full MIR on Windows. #![feature(rustc_private)] @@ -8,6 +7,16 @@ extern crate libc; fn main() { + test_mutex(); + #[cfg(not(target_os = "windows"))] // TODO: implement RwLock on Windows + { + test_rwlock_stdlib(); + test_rwlock_libc_init(); + test_rwlock_libc_static_initializer(); + } +} + +fn test_mutex() { let m = Mutex::new(0); { let _guard = m.lock(); @@ -15,54 +24,60 @@ fn main() { } drop(m.try_lock().unwrap()); drop(m); +} - #[cfg(not(target_os = "windows"))] // TODO: implement RwLock on Windows +#[cfg(not(target_os = "windows"))] +fn test_rwlock_stdlib() { + let rw = RwLock::new(0); { - let rw = RwLock::new(0); - { - let _read_guard = rw.read().unwrap(); - drop(rw.read().unwrap()); - drop(rw.try_read().unwrap()); - assert!(rw.try_write().unwrap_err().would_block()); - } + let _read_guard = rw.read().unwrap(); + drop(rw.read().unwrap()); + drop(rw.try_read().unwrap()); + assert!(rw.try_write().unwrap_err().would_block()); + } - { - let _write_guard = rw.write().unwrap(); - assert!(rw.try_read().unwrap_err().would_block()); - assert!(rw.try_write().unwrap_err().would_block()); - } + { + let _write_guard = rw.write().unwrap(); + assert!(rw.try_read().unwrap_err().would_block()); + assert!(rw.try_write().unwrap_err().would_block()); + } +} - // need to go a layer deeper and test the behavior of libc functions, because - // std::sys::unix::rwlock::RWLock keeps track of write_locked and num_readers +// need to go a layer deeper and test the behavior of libc functions, because +// std::sys::unix::rwlock::RWLock keeps track of write_locked and num_readers - unsafe { - let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); - assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null_mut()), 0); - assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY); - assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); - assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0); - assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); - assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0); - } +#[cfg(not(target_os = "windows"))] +fn test_rwlock_libc_init() { + unsafe { + let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); + assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null_mut()), 0); + assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY); + assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); + assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0); + assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); + assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0); + } +} - let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); - unsafe { - assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); - assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); +#[cfg(not(target_os = "windows"))] +fn test_rwlock_libc_static_initializer() { + let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); + unsafe { + assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); + assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY); - assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); - assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0); + assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY); + assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); + assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); - assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0); - } + assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0); } }