Cleanup Condvar tests.

This commit is contained in:
Vytautas Astrauskas 2020-04-30 15:37:27 -07:00 committed by Vytautas Astrauskas
parent 4a303b1309
commit 86eb262e8a
4 changed files with 103 additions and 179 deletions

View File

@ -1,4 +1,5 @@
use std::time::{Duration, SystemTime};
use std::convert::TryInto;
use rustc_middle::ty::{layout::TyAndLayout, TyKind, TypeAndMut};
use rustc_target::abi::{LayoutOf, Size};
@ -719,12 +720,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let mut offset = Size::from_bytes(0);
let layout = this.libc_ty_layout("time_t")?;
let seconds_place = tp.offset(offset, MemPlaceMeta::None, layout, this)?;
let seconds = this.read_scalar(seconds_place.into())?.to_u64()?;
let seconds = this.read_scalar(seconds_place.into())?;
offset += layout.size;
let layout = this.libc_ty_layout("c_long")?;
let nanoseconds_place = tp.offset(offset, MemPlaceMeta::None, layout, this)?;
let nanoseconds = this.read_scalar(nanoseconds_place.into())?.to_u64()?;
Duration::new(seconds, nanoseconds as u32)
let nanoseconds = this.read_scalar(nanoseconds_place.into())?;
let (seconds, nanoseconds) = if this.pointer_size().bytes() == 8 {
(seconds.to_u64()?, nanoseconds.to_u64()?.try_into().unwrap())
} else {
(seconds.to_u32()?.into(), nanoseconds.to_u32()?)
};
Duration::new(seconds, nanoseconds)
};
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {

View File

@ -1,199 +1,75 @@
// ignore-windows: No libc on Windows
// ignore-macos: pthread_condattr_setclock is not supported on MacOS.
// compile-flags: -Zmiri-disable-isolation
#![feature(rustc_private)]
/// Test that conditional variable timeouts are working properly with both
/// monotonic and system clocks.
extern crate libc;
use std::cell::UnsafeCell;
use std::mem::{self, MaybeUninit};
use std::sync::Arc;
use std::thread;
use std::mem;
use std::time::Instant;
struct Mutex {
inner: UnsafeCell<libc::pthread_mutex_t>,
}
fn test_timed_wait_timeout_monotonic() {
unsafe {
let mut attr: libc::pthread_condattr_t = mem::zeroed();
assert_eq!(libc::pthread_condattr_init(&mut attr as *mut _), 0);
assert_eq!(libc::pthread_condattr_setclock(&mut attr as *mut _, libc::CLOCK_MONOTONIC), 0);
unsafe impl Sync for Mutex {}
impl std::fmt::Debug for Mutex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Mutex")
}
}
struct Cond {
inner: UnsafeCell<libc::pthread_cond_t>,
}
unsafe impl Sync for Cond {}
impl std::fmt::Debug for Cond {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Cond")
}
}
unsafe fn create_cond_attr_monotonic() -> libc::pthread_condattr_t {
let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
assert_eq!(libc::pthread_condattr_init(attr.as_mut_ptr()), 0);
assert_eq!(libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC), 0);
attr.assume_init()
}
unsafe fn create_cond(attr: Option<libc::pthread_condattr_t>) -> Cond {
let cond: Cond = mem::zeroed();
if let Some(mut attr) = attr {
assert_eq!(libc::pthread_cond_init(cond.inner.get() as *mut _, &attr as *const _), 0);
let mut cond: libc::pthread_cond_t = mem::zeroed();
assert_eq!(libc::pthread_cond_init(&mut cond as *mut _, &attr as *const _), 0);
assert_eq!(libc::pthread_condattr_destroy(&mut attr as *mut _), 0);
} else {
assert_eq!(libc::pthread_cond_init(cond.inner.get() as *mut _, 0 as *const _), 0);
}
cond
}
unsafe fn create_mutex() -> Mutex {
mem::zeroed()
}
let mut mutex: libc::pthread_mutex_t = mem::zeroed();
unsafe fn create_timeout(seconds: i64) -> libc::timespec {
let mut now: libc::timespec = mem::zeroed();
assert_eq!(libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now), 0);
libc::timespec { tv_sec: now.tv_sec + seconds, tv_nsec: now.tv_nsec }
}
fn test_pthread_condattr_t() {
unsafe {
let mut attr = create_cond_attr_monotonic();
let mut clock_id = MaybeUninit::<libc::clockid_t>::uninit();
assert_eq!(libc::pthread_condattr_getclock(&attr as *const _, clock_id.as_mut_ptr()), 0);
assert_eq!(clock_id.assume_init(), libc::CLOCK_MONOTONIC);
assert_eq!(libc::pthread_condattr_destroy(&mut attr as *mut _), 0);
}
}
fn test_signal() {
unsafe {
let cond = Arc::new(create_cond(None));
let mutex = Arc::new(create_mutex());
assert_eq!(libc::pthread_mutex_lock(mutex.inner.get() as *mut _), 0);
let spawn_mutex = Arc::clone(&mutex);
let spawn_cond = Arc::clone(&cond);
let handle = thread::spawn(move || {
assert_eq!(libc::pthread_mutex_lock(spawn_mutex.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_cond_signal(spawn_cond.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_mutex_unlock(spawn_mutex.inner.get() as *mut _), 0);
});
let mut now: libc::timespec = mem::zeroed();
assert_eq!(libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now), 0);
let timeout = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: now.tv_nsec };
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
let current_time = Instant::now();
assert_eq!(
libc::pthread_cond_wait(cond.inner.get() as *mut _, mutex.inner.get() as *mut _),
0
);
assert_eq!(libc::pthread_mutex_unlock(mutex.inner.get() as *mut _), 0);
handle.join().unwrap();
let mutex = Arc::try_unwrap(mutex).unwrap();
assert_eq!(libc::pthread_mutex_destroy(mutex.inner.get() as *mut _), 0);
let cond = Arc::try_unwrap(cond).unwrap();
assert_eq!(libc::pthread_cond_destroy(cond.inner.get() as *mut _), 0);
}
}
fn test_broadcast() {
unsafe {
let cond = Arc::new(create_cond(None));
let mutex = Arc::new(create_mutex());
assert_eq!(libc::pthread_mutex_lock(mutex.inner.get() as *mut _), 0);
let spawn_mutex = Arc::clone(&mutex);
let spawn_cond = Arc::clone(&cond);
let handle = thread::spawn(move || {
assert_eq!(libc::pthread_mutex_lock(spawn_mutex.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_cond_broadcast(spawn_cond.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_mutex_unlock(spawn_mutex.inner.get() as *mut _), 0);
});
assert_eq!(
libc::pthread_cond_wait(cond.inner.get() as *mut _, mutex.inner.get() as *mut _),
0
);
assert_eq!(libc::pthread_mutex_unlock(mutex.inner.get() as *mut _), 0);
handle.join().unwrap();
let mutex = Arc::try_unwrap(mutex).unwrap();
assert_eq!(libc::pthread_mutex_destroy(mutex.inner.get() as *mut _), 0);
let cond = Arc::try_unwrap(cond).unwrap();
assert_eq!(libc::pthread_cond_destroy(cond.inner.get() as *mut _), 0);
}
}
fn test_timed_wait_timeout() {
unsafe {
let attr = create_cond_attr_monotonic();
let cond = create_cond(Some(attr));
let mutex = create_mutex();
let timeout = create_timeout(1);
assert_eq!(libc::pthread_mutex_lock(mutex.inner.get() as *mut _), 0);
assert_eq!(
libc::pthread_cond_timedwait(
cond.inner.get() as *mut _,
mutex.inner.get() as *mut _,
&timeout
),
libc::pthread_cond_timedwait(&mut cond as *mut _, &mut mutex as *mut _, &timeout),
libc::ETIMEDOUT
);
assert_eq!(libc::pthread_mutex_unlock(mutex.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_mutex_destroy(mutex.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_cond_destroy(cond.inner.get() as *mut _), 0);
assert!(current_time.elapsed().as_millis() >= 900);
assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
assert_eq!(libc::pthread_cond_destroy(&mut cond as *mut _), 0);
}
}
fn test_timed_wait_notimeout() {
fn test_timed_wait_timeout_realtime() {
unsafe {
let attr = create_cond_attr_monotonic();
let cond = Arc::new(create_cond(Some(attr)));
let mutex = Arc::new(create_mutex());
let timeout = create_timeout(100);
let mut attr: libc::pthread_condattr_t = mem::zeroed();
assert_eq!(libc::pthread_condattr_init(&mut attr as *mut _), 0);
assert_eq!(libc::pthread_condattr_setclock(&mut attr as *mut _, libc::CLOCK_REALTIME), 0);
assert_eq!(libc::pthread_mutex_lock(mutex.inner.get() as *mut _), 0);
let mut cond: libc::pthread_cond_t = mem::zeroed();
assert_eq!(libc::pthread_cond_init(&mut cond as *mut _, &attr as *const _), 0);
assert_eq!(libc::pthread_condattr_destroy(&mut attr as *mut _), 0);
let spawn_mutex = Arc::clone(&mutex);
let spawn_cond = Arc::clone(&cond);
let handle = thread::spawn(move || {
assert_eq!(libc::pthread_mutex_lock(spawn_mutex.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_cond_signal(spawn_cond.inner.get() as *mut _), 0);
assert_eq!(libc::pthread_mutex_unlock(spawn_mutex.inner.get() as *mut _), 0);
});
let mut mutex: libc::pthread_mutex_t = mem::zeroed();
let mut now: libc::timespec = mem::zeroed();
assert_eq!(libc::clock_gettime(libc::CLOCK_REALTIME, &mut now), 0);
let timeout = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: now.tv_nsec };
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
let current_time = Instant::now();
assert_eq!(
libc::pthread_cond_timedwait(
cond.inner.get() as *mut _,
mutex.inner.get() as *mut _,
&timeout
),
0
libc::pthread_cond_timedwait(&mut cond as *mut _, &mut mutex as *mut _, &timeout),
libc::ETIMEDOUT
);
assert_eq!(libc::pthread_mutex_unlock(mutex.inner.get() as *mut _), 0);
handle.join().unwrap();
let mutex = Arc::try_unwrap(mutex).unwrap();
assert_eq!(libc::pthread_mutex_destroy(mutex.inner.get() as *mut _), 0);
let cond = Arc::try_unwrap(cond).unwrap();
assert_eq!(libc::pthread_cond_destroy(cond.inner.get() as *mut _), 0);
assert!(current_time.elapsed().as_millis() >= 900);
assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
assert_eq!(libc::pthread_cond_destroy(&mut cond as *mut _), 0);
}
}
fn main() {
test_pthread_condattr_t();
test_signal();
test_broadcast();
test_timed_wait_timeout();
test_timed_wait_notimeout();
test_timed_wait_timeout_monotonic();
test_timed_wait_timeout_realtime();
}

View File

@ -1,2 +0,0 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.

View File

@ -31,7 +31,7 @@ fn check_barriers() {
// Check if Rust conditional variables are working.
/// The test taken from the Rust documentation.
fn check_conditional_variables() {
fn check_conditional_variables_notify_one() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
@ -52,17 +52,59 @@ fn check_conditional_variables() {
}
}
/// The test taken from the Rust documentation.
fn check_conditional_variables_notify_all() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
thread::spawn(move || {
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = true;
// We notify the condvar that the value has changed.
cvar.notify_all();
});
// Wait for the thread to start up.
let (lock, cvar) = &*pair;
let mut started = lock.lock().unwrap();
// As long as the value inside the `Mutex<bool>` is `false`, we wait.
while !*started {
started = cvar.wait(started).unwrap();
}
}
/// Test that waiting on a conditional variable with a timeout does not
/// deadlock.
fn check_conditional_variables_timeout() {
fn check_conditional_variables_timed_wait_timeout() {
let lock = Mutex::new(());
let cvar = Condvar::new();
let guard = lock.lock().unwrap();
let now = Instant::now();
let _guard = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap().0;
let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap();
assert!(timeout.timed_out());
assert!(now.elapsed().as_millis() >= 100);
}
/// Test that signaling a conditional variable when waiting with a timeout works
/// as expected.
fn check_conditional_variables_timed_wait_notimeout() {
let pair = Arc::new((Mutex::new(()), Condvar::new()));
let pair2 = pair.clone();
let (lock, cvar) = &*pair;
let guard = lock.lock().unwrap();
let handle = thread::spawn(move || {
let (_lock, cvar) = &*pair2;
cvar.notify_one();
});
let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap();
assert!(!timeout.timed_out());
handle.join().unwrap();
}
// Check if locks are working.
fn check_mutex() {
@ -218,8 +260,10 @@ fn check_once() {
fn main() {
check_barriers();
check_conditional_variables();
check_conditional_variables_timeout();
check_conditional_variables_notify_one();
check_conditional_variables_notify_all();
check_conditional_variables_timed_wait_timeout();
check_conditional_variables_timed_wait_notimeout();
check_mutex();
check_rwlock_write();
check_rwlock_read_no_deadlock();