Skip to content

Commit 27d9aff

Browse files
committed
std: fix busy-waiting in Once::wait_force, add more tests
1 parent 3e05ce4 commit 27d9aff

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

library/std/src/sync/once/tests.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use super::Once;
22
use crate::panic;
3+
use crate::sync::atomic::{AtomicBool, Ordering::Relaxed};
34
use crate::sync::mpsc::channel;
45
use crate::thread;
6+
use crate::time::Duration;
57

68
#[test]
79
fn smoke_once() {
@@ -114,3 +116,47 @@ fn wait_for_force_to_finish() {
114116
assert!(t1.join().is_ok());
115117
assert!(t2.join().is_ok());
116118
}
119+
120+
#[test]
121+
fn wait() {
122+
for _ in 0..50 {
123+
let val = AtomicBool::new(false);
124+
let once = Once::new();
125+
126+
thread::scope(|s| {
127+
for _ in 0..4 {
128+
s.spawn(|| {
129+
once.wait();
130+
assert!(val.load(Relaxed));
131+
});
132+
}
133+
134+
once.call_once(|| val.store(true, Relaxed));
135+
});
136+
}
137+
}
138+
139+
#[test]
140+
fn wait_on_poisoned() {
141+
let once = Once::new();
142+
143+
panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
144+
panic::catch_unwind(|| once.wait()).unwrap_err();
145+
}
146+
147+
#[test]
148+
fn wait_force_on_poisoned() {
149+
let once = Once::new();
150+
151+
thread::scope(|s| {
152+
panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
153+
154+
s.spawn(|| {
155+
thread::sleep(Duration::from_millis(100));
156+
157+
once.call_once_force(|_| {});
158+
});
159+
160+
once.wait_force();
161+
})
162+
}

library/std/src/sys/sync/once/queue.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl Once {
157157
panic!("Once instance has previously been poisoned");
158158
}
159159
_ => {
160-
current = wait(&self.state_and_queue, current);
160+
current = wait(&self.state_and_queue, current, !ignore_poisoning);
161161
}
162162
}
163163
}
@@ -220,14 +220,18 @@ impl Once {
220220
// All other values must be RUNNING with possibly a
221221
// pointer to the waiter queue in the more significant bits.
222222
assert!(state == RUNNING);
223-
current = wait(&self.state_and_queue, current);
223+
current = wait(&self.state_and_queue, current, true);
224224
}
225225
}
226226
}
227227
}
228228
}
229229

230-
fn wait(state_and_queue: &AtomicPtr<()>, mut current: StateAndQueue) -> StateAndQueue {
230+
fn wait(
231+
state_and_queue: &AtomicPtr<()>,
232+
mut current: StateAndQueue,
233+
return_on_poisoned: bool,
234+
) -> StateAndQueue {
231235
let node = &Waiter {
232236
thread: Cell::new(Some(thread::current())),
233237
signaled: AtomicBool::new(false),
@@ -239,7 +243,7 @@ fn wait(state_and_queue: &AtomicPtr<()>, mut current: StateAndQueue) -> StateAnd
239243
let queue = to_queue(current);
240244

241245
// If initialization has finished, return.
242-
if matches!(state, POISONED | COMPLETE) {
246+
if state == COMPLETE || (return_on_poisoned && state == POISONED) {
243247
return current;
244248
}
245249

0 commit comments

Comments
 (0)