add nonpoison and poison condvar tests
Adds tests for the `nonpoison::Mutex` variant by using a macro to duplicate the existing `poison` tests. Note that all of the tests here are adapted from the existing `poison` tests. Also steals the `test_mutex_arc_condvar` test from `mutex.rs`. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
This commit is contained in:
parent
eaf7fd2fed
commit
b2380d2bcc
3 changed files with 253 additions and 207 deletions
|
|
@ -1,190 +1,269 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let c = Condvar::new();
|
||||
c.notify_one();
|
||||
c.notify_all();
|
||||
}
|
||||
use super::nonpoison_and_poison_unwrap_test;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn notify_one() {
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: smoke,
|
||||
test_body: {
|
||||
use locks::Condvar;
|
||||
|
||||
let g = m.lock().unwrap();
|
||||
let _t = thread::spawn(move || {
|
||||
let _g = m2.lock().unwrap();
|
||||
c2.notify_one();
|
||||
});
|
||||
let g = c.wait(g).unwrap();
|
||||
drop(g);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn notify_all() {
|
||||
const N: usize = 10;
|
||||
|
||||
let data = Arc::new((Mutex::new(0), Condvar::new()));
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..N {
|
||||
let data = data.clone();
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
let mut cnt = lock.lock().unwrap();
|
||||
*cnt += 1;
|
||||
if *cnt == N {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
while *cnt != 0 {
|
||||
cnt = cond.wait(cnt).unwrap();
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
let c = Condvar::new();
|
||||
c.notify_one();
|
||||
c.notify_all();
|
||||
}
|
||||
drop(tx);
|
||||
);
|
||||
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
rx.recv().unwrap();
|
||||
let mut cnt = lock.lock().unwrap();
|
||||
*cnt = 0;
|
||||
cond.notify_all();
|
||||
drop(cnt);
|
||||
|
||||
for _ in 0..N {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn wait_while() {
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair2 = pair.clone();
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: notify_one,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
// Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair2;
|
||||
let mut started = lock.lock().unwrap();
|
||||
*started = true;
|
||||
// We notify the condvar that the value has changed.
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
// Wait for the thread to start up.
|
||||
let &(ref lock, ref cvar) = &*pair;
|
||||
let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started);
|
||||
assert!(*guard.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported
|
||||
fn wait_timeout_wait() {
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = m.lock().unwrap();
|
||||
let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not timeout
|
||||
if !no_timeout.timed_out() {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported
|
||||
fn wait_timeout_while_wait() {
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = m.lock().unwrap();
|
||||
let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap();
|
||||
// no spurious wakeups. ensure it timed-out
|
||||
assert!(wait.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported
|
||||
fn wait_timeout_while_instant_satisfy() {
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = m.lock().unwrap();
|
||||
let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap();
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn wait_timeout_while_wake() {
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair_copy = pair.clone();
|
||||
|
||||
let &(ref m, ref c) = &*pair;
|
||||
let g = m.lock().unwrap();
|
||||
let _t = thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair_copy;
|
||||
let mut started = lock.lock().unwrap();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
*started = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
let (g2, wait) = c
|
||||
.wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified)
|
||||
.unwrap();
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
assert!(*g2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn wait_timeout_wake() {
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = m.lock().unwrap();
|
||||
|
||||
let c2 = c.clone();
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
|
||||
let notified = Arc::new(AtomicBool::new(false));
|
||||
let notified_copy = notified.clone();
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let _g = m2.lock().unwrap();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
notified_copy.store(true, Ordering::Relaxed);
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let _t = thread::spawn(move || {
|
||||
let _g = maybe_unwrap(m2.lock());
|
||||
c2.notify_one();
|
||||
});
|
||||
let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap();
|
||||
assert!(!timeout_res.timed_out());
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not notified
|
||||
if !notified.load(Ordering::Relaxed) {
|
||||
t.join().unwrap();
|
||||
continue;
|
||||
}
|
||||
let g = maybe_unwrap(c.wait(g));
|
||||
drop(g);
|
||||
|
||||
t.join().unwrap();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: notify_all,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
const N: usize = 10;
|
||||
|
||||
let data = Arc::new((Mutex::new(0), Condvar::new()));
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..N {
|
||||
let data = data.clone();
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
let mut cnt = maybe_unwrap(lock.lock());
|
||||
*cnt += 1;
|
||||
if *cnt == N {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
while *cnt != 0 {
|
||||
cnt = maybe_unwrap(cond.wait(cnt));
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
rx.recv().unwrap();
|
||||
let mut cnt = maybe_unwrap(lock.lock());
|
||||
*cnt = 0;
|
||||
cond.notify_all();
|
||||
drop(cnt);
|
||||
|
||||
for _ in 0..N {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: test_mutex_arc_condvar,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
// Wait until our parent has taken the lock.
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
|
||||
// Set the data to `true` and wake up our parent.
|
||||
let mut guard = maybe_unwrap(lock.lock());
|
||||
*guard = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut guard = maybe_unwrap(lock.lock());
|
||||
// Wake up our child.
|
||||
tx.send(()).unwrap();
|
||||
|
||||
// Wait until our child has set the data to `true`.
|
||||
assert!(!*guard);
|
||||
while !*guard {
|
||||
guard = maybe_unwrap(cvar.wait(guard));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_while,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair2 = pair.clone();
|
||||
|
||||
// Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair2;
|
||||
let mut started = maybe_unwrap(lock.lock());
|
||||
*started = true;
|
||||
// We notify the condvar that the value has changed.
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
// Wait for the thread to start up.
|
||||
let &(ref lock, ref cvar) = &*pair;
|
||||
let guard = cvar.wait_while(maybe_unwrap(lock.lock()), |started| !*started);
|
||||
assert!(*maybe_unwrap(guard));
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_wait,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let (_g, no_timeout) = maybe_unwrap(c.wait_timeout(g, Duration::from_millis(1)));
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not timeout
|
||||
if !no_timeout.timed_out() {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_while_wait,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let (_g, wait) = maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(1), |_| true));
|
||||
// no spurious wakeups. ensure it timed-out
|
||||
assert!(wait.timed_out());
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_while_instant_satisfy,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let (_g, wait) =
|
||||
maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(0), |_| false));
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_while_wake,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair_copy = pair.clone();
|
||||
|
||||
let &(ref m, ref c) = &*pair;
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let _t = thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair_copy;
|
||||
let mut started = maybe_unwrap(lock.lock());
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
*started = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
let (g2, wait) = maybe_unwrap(c.wait_timeout_while(
|
||||
g,
|
||||
Duration::from_millis(u64::MAX),
|
||||
|&mut notified| !notified
|
||||
));
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
assert!(*g2);
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_wake,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = maybe_unwrap(m.lock());
|
||||
|
||||
let c2 = c.clone();
|
||||
let m2 = m.clone();
|
||||
|
||||
let notified = Arc::new(AtomicBool::new(false));
|
||||
let notified_copy = notified.clone();
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let _g = maybe_unwrap(m2.lock());
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
notified_copy.store(true, Ordering::Relaxed);
|
||||
c2.notify_one();
|
||||
});
|
||||
let (g, timeout_res) =
|
||||
maybe_unwrap(c.wait_timeout(g, Duration::from_millis(u64::MAX)));
|
||||
assert!(!timeout_res.timed_out());
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not notified
|
||||
if !notified.load(Ordering::Relaxed) {
|
||||
t.join().unwrap();
|
||||
continue;
|
||||
}
|
||||
drop(g);
|
||||
|
||||
t.join().unwrap();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#![feature(rwlock_downgrade)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(sync_nonpoison)]
|
||||
#![feature(nonpoison_condvar)]
|
||||
#![feature(nonpoison_mutex)]
|
||||
#![feature(nonpoison_rwlock)]
|
||||
#![allow(internal_features)]
|
||||
|
|
|
|||
|
|
@ -213,40 +213,6 @@ nonpoison_and_poison_unwrap_test!(
|
|||
}
|
||||
);
|
||||
|
||||
// FIXME(nonpoison_condvar): Move this to the `condvar.rs` test file once `nonpoison::condvar` gets
|
||||
// implemented.
|
||||
#[test]
|
||||
fn test_mutex_arc_condvar() {
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
// Wait until our parent has taken the lock.
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
|
||||
// Set the data to `true` and wake up our parent.
|
||||
let mut guard = lock.lock().unwrap();
|
||||
*guard = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut guard = lock.lock().unwrap();
|
||||
// Wake up our child.
|
||||
tx.send(()).unwrap();
|
||||
|
||||
// Wait until our child has set the data to `true`.
|
||||
assert!(!*guard);
|
||||
while !*guard {
|
||||
guard = cvar.wait(guard).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: test_mutex_arc_nested,
|
||||
test_body: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue