Skip to content

Commit 1d49aad

Browse files
committed
std: fix busy-waiting in Once::wait_force, add more tests
1 parent cf11f49 commit 1d49aad

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

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

+47
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use super::Once;
2+
use crate::sync::atomic::AtomicBool;
3+
use crate::sync::atomic::Ordering::Relaxed;
24
use crate::sync::mpsc::channel;
5+
use crate::time::Duration;
36
use crate::{panic, thread};
47

58
#[test]
@@ -113,3 +116,47 @@ fn wait_for_force_to_finish() {
113116
assert!(t1.join().is_ok());
114117
assert!(t2.join().is_ok());
115118
}
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
@@ -153,7 +153,7 @@ impl Once {
153153
panic!("Once instance has previously been poisoned");
154154
}
155155
_ => {
156-
current = wait(&self.state_and_queue, current);
156+
current = wait(&self.state_and_queue, current, !ignore_poisoning);
157157
}
158158
}
159159
}
@@ -216,14 +216,18 @@ impl Once {
216216
// All other values must be RUNNING with possibly a
217217
// pointer to the waiter queue in the more significant bits.
218218
assert!(state == RUNNING);
219-
current = wait(&self.state_and_queue, current);
219+
current = wait(&self.state_and_queue, current, true);
220220
}
221221
}
222222
}
223223
}
224224
}
225225

226-
fn wait(state_and_queue: &AtomicPtr<()>, mut current: StateAndQueue) -> StateAndQueue {
226+
fn wait(
227+
state_and_queue: &AtomicPtr<()>,
228+
mut current: StateAndQueue,
229+
return_on_poisoned: bool,
230+
) -> StateAndQueue {
227231
let node = &Waiter {
228232
thread: Cell::new(Some(thread::current())),
229233
signaled: AtomicBool::new(false),
@@ -235,7 +239,7 @@ fn wait(state_and_queue: &AtomicPtr<()>, mut current: StateAndQueue) -> StateAnd
235239
let queue = to_queue(current);
236240

237241
// If initialization has finished, return.
238-
if matches!(state, POISONED | COMPLETE) {
242+
if state == COMPLETE || (return_on_poisoned && state == POISONED) {
239243
return current;
240244
}
241245

0 commit comments

Comments
 (0)