mirror of
https://github.com/rust-lang/rust.git
synced 2025-11-24 05:57:07 +00:00
599 lines
17 KiB
Rust
599 lines
17 KiB
Rust
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use std::sync::mpsc::channel;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
use super::nonpoison_and_poison_unwrap_test;
|
|
|
|
nonpoison_and_poison_unwrap_test!(
|
|
name: smoke,
|
|
test_body: {
|
|
use locks::Condvar;
|
|
|
|
let c = Condvar::new();
|
|
c.notify_one();
|
|
c.notify_all();
|
|
}
|
|
);
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_notify_one() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let m2 = m.clone();
|
|
let c = Arc::new(Condvar::new());
|
|
let c2 = c.clone();
|
|
|
|
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(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_notify_one() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let m2 = m.clone();
|
|
let c = Arc::new(Condvar::new());
|
|
let c2 = c.clone();
|
|
|
|
let mut g = m.lock();
|
|
let _t = thread::spawn(move || {
|
|
let _g = m2.lock();
|
|
c2.notify_one();
|
|
});
|
|
|
|
c.wait(&mut g);
|
|
drop(g);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_notify_all() {
|
|
use std::sync::poison::{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 = lock.lock().unwrap();
|
|
*cnt += 1;
|
|
if *cnt == N {
|
|
tx.send(()).unwrap();
|
|
}
|
|
while *cnt != 0 {
|
|
cnt = cond.wait(cnt).unwrap();
|
|
}
|
|
tx.send(()).unwrap();
|
|
});
|
|
}
|
|
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(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_notify_all() {
|
|
use std::sync::nonpoison::{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 = lock.lock();
|
|
*cnt += 1;
|
|
if *cnt == N {
|
|
tx.send(()).unwrap();
|
|
}
|
|
while *cnt != 0 {
|
|
cond.wait(&mut cnt);
|
|
}
|
|
tx.send(()).unwrap();
|
|
});
|
|
}
|
|
drop(tx);
|
|
|
|
let &(ref lock, ref cond) = &*data;
|
|
rx.recv().unwrap();
|
|
let mut cnt = lock.lock();
|
|
*cnt = 0;
|
|
cond.notify_all();
|
|
drop(cnt);
|
|
|
|
for _ in 0..N {
|
|
rx.recv().unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_test_mutex_arc_condvar() {
|
|
use std::sync::poison::{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 = 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();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_test_mutex_arc_condvar() {
|
|
use std::sync::nonpoison::{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 = lock.lock();
|
|
*guard = true;
|
|
cvar.notify_one();
|
|
});
|
|
|
|
let &(ref lock, ref cvar) = &*packet.0;
|
|
let mut guard = lock.lock();
|
|
// Wake up our child.
|
|
tx.send(()).unwrap();
|
|
|
|
// Wait until our child has set the data to `true`.
|
|
assert!(!*guard);
|
|
while !*guard {
|
|
cvar.wait(&mut guard);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_wait_while() {
|
|
use std::sync::poison::{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 = 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).unwrap();
|
|
assert!(*guard);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_wait_while() {
|
|
use std::sync::nonpoison::{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 = 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 mut guard = lock.lock();
|
|
cvar.wait_while(&mut guard, |started| !*started);
|
|
assert!(*guard);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_wait_timeout_wait() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
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(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_wait_timeout_wait() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let c = Arc::new(Condvar::new());
|
|
|
|
loop {
|
|
let mut g = m.lock();
|
|
let no_timeout = c.wait_timeout(&mut 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;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_wait_timeout_while_wait() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
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(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_wait_timeout_while_wait() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let c = Arc::new(Condvar::new());
|
|
|
|
let mut g = m.lock();
|
|
let wait = c.wait_timeout_while(&mut g, Duration::from_millis(1), |_| true);
|
|
// no spurious wakeups. ensure it timed-out
|
|
assert!(wait.timed_out());
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_wait_timeout_while_instant_satisfy() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
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(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_wait_timeout_while_instant_satisfy() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let c = Arc::new(Condvar::new());
|
|
|
|
let mut g = m.lock();
|
|
let wait = c.wait_timeout_while(&mut g, Duration::from_millis(0), |_| false);
|
|
// ensure it didn't time-out even if we were not given any time.
|
|
assert!(!wait.timed_out());
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_wait_timeout_while_wake() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
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(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_wait_timeout_while_wake() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
|
let pair_copy = pair.clone();
|
|
|
|
let &(ref m, ref c) = &*pair;
|
|
let mut g = m.lock();
|
|
let _t = thread::spawn(move || {
|
|
let &(ref lock, ref cvar) = &*pair_copy;
|
|
let mut started = lock.lock();
|
|
thread::sleep(Duration::from_millis(1));
|
|
*started = true;
|
|
cvar.notify_one();
|
|
});
|
|
|
|
let wait =
|
|
c.wait_timeout_while(&mut 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!(*g);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn poison_wait_timeout_wake() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let c = Arc::new(Condvar::new());
|
|
|
|
loop {
|
|
let g = m.lock().unwrap();
|
|
|
|
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 = m2.lock().unwrap();
|
|
thread::sleep(Duration::from_millis(1));
|
|
notified_copy.store(true, Ordering::Relaxed);
|
|
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;
|
|
}
|
|
drop(g);
|
|
|
|
t.join().unwrap();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
|
fn nonpoison_wait_timeout_wake() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let m = Arc::new(Mutex::new(()));
|
|
let c = Arc::new(Condvar::new());
|
|
|
|
loop {
|
|
let mut g = 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 = m2.lock();
|
|
thread::sleep(Duration::from_millis(1));
|
|
notified_copy.store(true, Ordering::Relaxed);
|
|
c2.notify_one();
|
|
});
|
|
|
|
let timeout_res = c.wait_timeout(&mut 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;
|
|
}
|
|
}
|
|
|
|
// Some platforms internally cast the timeout duration into nanoseconds.
|
|
// If they fail to consider overflow during the conversion (I'm looking
|
|
// at you, macOS), `wait_timeout` will return immediately and indicate a
|
|
// timeout for durations that are slightly longer than u64::MAX nanoseconds.
|
|
// `std` should guard against this by clamping the timeout.
|
|
// See #37440 for context.
|
|
#[test]
|
|
fn poison_timeout_nanoseconds() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
let sent = Mutex::new(false);
|
|
let cond = Condvar::new();
|
|
|
|
thread::scope(|s| {
|
|
s.spawn(|| {
|
|
// Sleep so that the other thread has a chance to encounter the
|
|
// timeout.
|
|
thread::sleep(Duration::from_secs(2));
|
|
*sent.lock().unwrap() = true;
|
|
cond.notify_all();
|
|
});
|
|
|
|
let mut guard = sent.lock().unwrap();
|
|
// Loop until `sent` is set by the thread to guard against spurious
|
|
// wakeups. If the `wait_timeout` happens just before the signal by
|
|
// the other thread, such a spurious wakeup might prevent the
|
|
// miscalculated timeout from occurring, but this is basically just
|
|
// a smoke test anyway.
|
|
loop {
|
|
if *guard {
|
|
break;
|
|
}
|
|
|
|
// If there is internal overflow, this call will return almost
|
|
// immediately, before the other thread has reached the `notify_all`,
|
|
// and indicate a timeout.
|
|
let (g, res) = cond
|
|
.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)))
|
|
.unwrap();
|
|
assert!(!res.timed_out());
|
|
guard = g;
|
|
}
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn nonpoison_timeout_nanoseconds() {
|
|
use std::sync::nonpoison::{Condvar, Mutex};
|
|
|
|
let sent = Mutex::new(false);
|
|
let cond = Condvar::new();
|
|
|
|
thread::scope(|s| {
|
|
s.spawn(|| {
|
|
// Sleep so that the other thread has a chance to encounter the
|
|
// timeout.
|
|
thread::sleep(Duration::from_secs(2));
|
|
sent.set(true);
|
|
cond.notify_all();
|
|
});
|
|
|
|
let mut guard = sent.lock();
|
|
// Loop until `sent` is set by the thread to guard against spurious
|
|
// wakeups. If the `wait_timeout` happens just before the signal by
|
|
// the other thread, such a spurious wakeup might prevent the
|
|
// miscalculated timeout from occurring, but this is basically just
|
|
// a smoke test anyway.
|
|
loop {
|
|
if *guard {
|
|
break;
|
|
}
|
|
|
|
// If there is internal overflow, this call will return almost
|
|
// immediately, before the other thread has reached the `notify_all`,
|
|
// and indicate a timeout.
|
|
let res = cond
|
|
.wait_timeout(&mut guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)));
|
|
assert!(!res.timed_out());
|
|
}
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
|
fn test_arc_condvar_poison() {
|
|
use std::sync::poison::{Condvar, Mutex};
|
|
|
|
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
|
|
|
let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
|
|
let packet2 = Packet(packet.0.clone());
|
|
let (tx, rx) = channel();
|
|
|
|
let _t = thread::spawn(move || -> () {
|
|
rx.recv().unwrap();
|
|
let &(ref lock, ref cvar) = &*packet2.0;
|
|
let _g = lock.lock().unwrap();
|
|
cvar.notify_one();
|
|
// Parent should fail when it wakes up.
|
|
panic!();
|
|
});
|
|
|
|
let &(ref lock, ref cvar) = &*packet.0;
|
|
let mut lock = lock.lock().unwrap();
|
|
tx.send(()).unwrap();
|
|
while *lock == 1 {
|
|
match cvar.wait(lock) {
|
|
Ok(l) => {
|
|
lock = l;
|
|
assert_eq!(*lock, 1);
|
|
}
|
|
Err(..) => break,
|
|
}
|
|
}
|
|
}
|