2025-01-17 11:04:40 +00:00
|
|
|
use std::sync::Once;
|
|
|
|
use std::sync::atomic::AtomicBool;
|
|
|
|
use std::sync::atomic::Ordering::Relaxed;
|
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
use std::time::Duration;
|
|
|
|
use std::{panic, thread};
|
2020-08-27 13:45:01 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn smoke_once() {
|
|
|
|
static O: Once = Once::new();
|
|
|
|
let mut a = 0;
|
|
|
|
O.call_once(|| a += 1);
|
|
|
|
assert_eq!(a, 1);
|
|
|
|
O.call_once(|| a += 1);
|
|
|
|
assert_eq!(a, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn stampede_once() {
|
|
|
|
static O: Once = Once::new();
|
|
|
|
static mut RUN: bool = false;
|
|
|
|
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
for _ in 0..10 {
|
|
|
|
let tx = tx.clone();
|
|
|
|
thread::spawn(move || {
|
|
|
|
for _ in 0..4 {
|
|
|
|
thread::yield_now()
|
|
|
|
}
|
|
|
|
unsafe {
|
|
|
|
O.call_once(|| {
|
|
|
|
assert!(!RUN);
|
|
|
|
RUN = true;
|
|
|
|
});
|
|
|
|
assert!(RUN);
|
|
|
|
}
|
|
|
|
tx.send(()).unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
O.call_once(|| {
|
|
|
|
assert!(!RUN);
|
|
|
|
RUN = true;
|
|
|
|
});
|
|
|
|
assert!(RUN);
|
|
|
|
}
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
rx.recv().unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn poison_bad() {
|
|
|
|
static O: Once = Once::new();
|
|
|
|
|
|
|
|
// poison the once
|
|
|
|
let t = panic::catch_unwind(|| {
|
|
|
|
O.call_once(|| panic!());
|
|
|
|
});
|
|
|
|
assert!(t.is_err());
|
|
|
|
|
|
|
|
// poisoning propagates
|
|
|
|
let t = panic::catch_unwind(|| {
|
|
|
|
O.call_once(|| {});
|
|
|
|
});
|
|
|
|
assert!(t.is_err());
|
|
|
|
|
|
|
|
// we can subvert poisoning, however
|
|
|
|
let mut called = false;
|
|
|
|
O.call_once_force(|p| {
|
|
|
|
called = true;
|
2021-02-04 10:13:03 +00:00
|
|
|
assert!(p.is_poisoned())
|
2020-08-27 13:45:01 +00:00
|
|
|
});
|
|
|
|
assert!(called);
|
|
|
|
|
|
|
|
// once any success happens, we stop propagating the poison
|
|
|
|
O.call_once(|| {});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn wait_for_force_to_finish() {
|
|
|
|
static O: Once = Once::new();
|
|
|
|
|
|
|
|
// poison the once
|
|
|
|
let t = panic::catch_unwind(|| {
|
|
|
|
O.call_once(|| panic!());
|
|
|
|
});
|
|
|
|
assert!(t.is_err());
|
|
|
|
|
|
|
|
// make sure someone's waiting inside the once via a force
|
|
|
|
let (tx1, rx1) = channel();
|
|
|
|
let (tx2, rx2) = channel();
|
|
|
|
let t1 = thread::spawn(move || {
|
|
|
|
O.call_once_force(|p| {
|
2021-02-04 10:13:03 +00:00
|
|
|
assert!(p.is_poisoned());
|
2020-08-27 13:45:01 +00:00
|
|
|
tx1.send(()).unwrap();
|
|
|
|
rx2.recv().unwrap();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
rx1.recv().unwrap();
|
|
|
|
|
|
|
|
// put another waiter on the once
|
|
|
|
let t2 = thread::spawn(|| {
|
|
|
|
let mut called = false;
|
|
|
|
O.call_once(|| {
|
|
|
|
called = true;
|
|
|
|
});
|
|
|
|
assert!(!called);
|
|
|
|
});
|
|
|
|
|
|
|
|
tx2.send(()).unwrap();
|
|
|
|
|
|
|
|
assert!(t1.join().is_ok());
|
|
|
|
assert!(t2.join().is_ok());
|
|
|
|
}
|
2024-07-12 11:17:43 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn wait() {
|
|
|
|
for _ in 0..50 {
|
|
|
|
let val = AtomicBool::new(false);
|
|
|
|
let once = Once::new();
|
|
|
|
|
|
|
|
thread::scope(|s| {
|
|
|
|
for _ in 0..4 {
|
|
|
|
s.spawn(|| {
|
|
|
|
once.wait();
|
|
|
|
assert!(val.load(Relaxed));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
once.call_once(|| val.store(true, Relaxed));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn wait_on_poisoned() {
|
|
|
|
let once = Once::new();
|
|
|
|
|
|
|
|
panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
|
|
|
|
panic::catch_unwind(|| once.wait()).unwrap_err();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn wait_force_on_poisoned() {
|
|
|
|
let once = Once::new();
|
|
|
|
|
|
|
|
thread::scope(|s| {
|
|
|
|
panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
|
|
|
|
|
|
|
|
s.spawn(|| {
|
|
|
|
thread::sleep(Duration::from_millis(100));
|
|
|
|
|
|
|
|
once.call_once_force(|_| {});
|
|
|
|
});
|
|
|
|
|
|
|
|
once.wait_force();
|
|
|
|
})
|
|
|
|
}
|