Cleanup Condvar tests.
This commit is contained in:
parent
4a303b1309
commit
86eb262e8a
@ -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")? {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user