1
Fork 0

std: fix busy-waiting in Once::wait_force, add more tests

This commit is contained in:
joboet 2024-07-12 13:17:43 +02:00
parent cf11f499b3
commit 1d49aad844
No known key found for this signature in database
GPG key ID: 704E0149B0194B3C
2 changed files with 55 additions and 4 deletions

View file

@ -1,5 +1,8 @@
use super::Once;
use crate::sync::atomic::AtomicBool;
use crate::sync::atomic::Ordering::Relaxed;
use crate::sync::mpsc::channel;
use crate::time::Duration;
use crate::{panic, thread};
#[test]
@ -113,3 +116,47 @@ fn wait_for_force_to_finish() {
assert!(t1.join().is_ok());
assert!(t2.join().is_ok());
}
#[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();
})
}

View file

@ -153,7 +153,7 @@ impl Once {
panic!("Once instance has previously been poisoned");
}
_ => {
current = wait(&self.state_and_queue, current);
current = wait(&self.state_and_queue, current, !ignore_poisoning);
}
}
}
@ -216,14 +216,18 @@ impl Once {
// All other values must be RUNNING with possibly a
// pointer to the waiter queue in the more significant bits.
assert!(state == RUNNING);
current = wait(&self.state_and_queue, current);
current = wait(&self.state_and_queue, current, true);
}
}
}
}
}
fn wait(state_and_queue: &AtomicPtr<()>, mut current: StateAndQueue) -> StateAndQueue {
fn wait(
state_and_queue: &AtomicPtr<()>,
mut current: StateAndQueue,
return_on_poisoned: bool,
) -> StateAndQueue {
let node = &Waiter {
thread: Cell::new(Some(thread::current())),
signaled: AtomicBool::new(false),
@ -235,7 +239,7 @@ fn wait(state_and_queue: &AtomicPtr<()>, mut current: StateAndQueue) -> StateAnd
let queue = to_queue(current);
// If initialization has finished, return.
if matches!(state, POISONED | COMPLETE) {
if state == COMPLETE || (return_on_poisoned && state == POISONED) {
return current;
}