Skip to content

Commit 078aef6

Browse files
committed
add nonpoison::once implementation
Adds the equivalent `nonpoison` types to the `poison::once` module. These types and implementations are gated under the `nonpoison_once` feature gate.
1 parent 7278554 commit 078aef6

File tree

2 files changed

+245
-0
lines changed

2 files changed

+245
-0
lines changed

library/std/src/sync/nonpoison.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,8 @@ impl fmt::Display for WouldBlock {
3333
pub use self::mutex::MappedMutexGuard;
3434
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
3535
pub use self::mutex::{Mutex, MutexGuard};
36+
#[unstable(feature = "nonpoison_once", issue = "134645")]
37+
pub use self::once::Once;
3638

3739
mod mutex;
40+
pub(crate) mod once;
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
use crate::fmt;
2+
use crate::sys::sync as sys;
3+
use crate::sys::sync::once::ExclusiveState;
4+
5+
/// A low-level synchronization primitive for one-time global execution that ignores poisoning.
6+
///
7+
/// For more information about `Once`, check out the documentation for the poisoning variant (which
8+
/// can be found at [`poison::Once`]).
9+
///
10+
/// [`poison::Once`]: crate::sync::poison::Once
11+
///
12+
/// # Examples
13+
///
14+
/// ```
15+
/// use std::sync::Once;
16+
///
17+
/// static START: Once = Once::new();
18+
///
19+
/// START.call_once(|| {
20+
/// // run initialization here
21+
/// });
22+
/// ```
23+
#[unstable(feature = "nonpoison_once", issue = "134645")]
24+
pub struct Once {
25+
inner: sys::Once,
26+
}
27+
28+
impl Once {
29+
/// Creates a new `Once` value.
30+
#[inline]
31+
#[unstable(feature = "nonpoison_once", issue = "134645")]
32+
#[must_use]
33+
pub const fn new() -> Once {
34+
Once { inner: sys::Once::new() }
35+
}
36+
37+
/// Performs an initialization routine once and only once. The given closure
38+
/// will be executed if this is the first time `call_once` has been called,
39+
/// and otherwise the routine will *not* be invoked.
40+
///
41+
/// This method will block the calling thread if another initialization
42+
/// routine is currently running.
43+
///
44+
/// When this function returns, it is guaranteed that some initialization
45+
/// has run and completed (it might not be the closure specified). It is also
46+
/// guaranteed that any memory writes performed by the executed closure can
47+
/// be reliably observed by other threads at this point (there is a
48+
/// happens-before relation between the closure and code executing after the
49+
/// return).
50+
///
51+
/// If the given closure recursively invokes `call_once` on the same [`Once`]
52+
/// instance, the exact behavior is not specified: allowed outcomes are
53+
/// a panic or a deadlock.
54+
///
55+
/// # Examples
56+
///
57+
/// ```
58+
/// #![feature(nonpoison_once)]
59+
///
60+
/// use std::sync::nonpoison::Once;
61+
///
62+
/// static mut VAL: usize = 0;
63+
/// static INIT: Once = Once::new();
64+
///
65+
/// // Accessing a `static mut` is unsafe much of the time, but if we do so
66+
/// // in a synchronized fashion (e.g., write once or read all) then we're
67+
/// // good to go!
68+
/// //
69+
/// // This function will only call `expensive_computation` once, and will
70+
/// // otherwise always return the value returned from the first invocation.
71+
/// fn get_cached_val() -> usize {
72+
/// unsafe {
73+
/// INIT.call_once(|| {
74+
/// VAL = expensive_computation();
75+
/// });
76+
/// VAL
77+
/// }
78+
/// }
79+
///
80+
/// fn expensive_computation() -> usize {
81+
/// // ...
82+
/// # 2
83+
/// }
84+
/// ```
85+
///
86+
/// # Panics
87+
///
88+
/// The closure `f` will only be executed once even if this is called concurrently amongst many
89+
/// threads. If the closure panics, the calling thread will panic and the `Once` will remain in
90+
/// an incompleted state.
91+
///
92+
/// In contrast to the [`poison::Once`] variant, all calls to `call_once` will ignore panics in
93+
/// other threads. This method is identical to the [`poison::Once::call_once_force`] method.
94+
///
95+
/// If you need observability into whether any threads have panicked while calling `call_once`,
96+
/// see [`poison::Once`].
97+
///
98+
/// [`poison::Once`]: crate::sync::poison::Once
99+
/// [`poison::Once::call_once_force`]: crate::sync::poison::Once::call_once_force
100+
///
101+
/// ```
102+
/// #![feature(nonpoison_once)]
103+
///
104+
/// use std::sync::nonpoison::Once;
105+
/// use std::thread;
106+
///
107+
/// static INIT: Once = Once::new();
108+
///
109+
/// // Panic during `call_once`.
110+
/// let handle = thread::spawn(|| {
111+
/// INIT.call_once(|| panic!());
112+
/// });
113+
/// assert!(handle.join().is_err());
114+
///
115+
/// // `call_once` will still run from a different thread.
116+
/// INIT.call_once(|| {
117+
/// assert_eq!(2 + 2, 4);
118+
/// });
119+
/// ```
120+
#[inline]
121+
#[unstable(feature = "nonpoison_once", issue = "134645")]
122+
#[track_caller]
123+
pub fn call_once<F>(&self, f: F)
124+
where
125+
F: FnOnce(),
126+
{
127+
// Fast path check.
128+
if self.inner.is_completed() {
129+
return;
130+
}
131+
132+
let mut f = Some(f);
133+
self.inner.call(true, &mut |_| f.take().unwrap()());
134+
}
135+
136+
/// Returns `true` if some [`call_once()`] call has completed
137+
/// successfully. Specifically, `is_completed` will return false in
138+
/// the following situations:
139+
/// * [`call_once()`] was not called at all,
140+
/// * [`call_once()`] was called, but has not yet completed
141+
///
142+
/// This function returning `false` does not mean that [`Once`] has not been
143+
/// executed. For example, it may have been executed in the time between
144+
/// when `is_completed` starts executing and when it returns, in which case
145+
/// the `false` return value would be stale (but still permissible).
146+
///
147+
/// [`call_once()`]: Once::call_once
148+
///
149+
/// # Examples
150+
///
151+
/// ```
152+
/// #![feature(nonpoison_once)]
153+
///
154+
/// use std::sync::nonpoison::Once;
155+
///
156+
/// static INIT: Once = Once::new();
157+
///
158+
/// assert_eq!(INIT.is_completed(), false);
159+
/// INIT.call_once(|| {
160+
/// assert_eq!(INIT.is_completed(), false);
161+
/// });
162+
/// assert_eq!(INIT.is_completed(), true);
163+
/// ```
164+
///
165+
/// ```
166+
/// #![feature(nonpoison_once)]
167+
///
168+
/// use std::sync::nonpoison::Once;
169+
/// use std::thread;
170+
///
171+
/// static INIT: Once = Once::new();
172+
///
173+
/// assert_eq!(INIT.is_completed(), false);
174+
/// let handle = thread::spawn(|| {
175+
/// INIT.call_once(|| panic!());
176+
/// });
177+
/// assert!(handle.join().is_err());
178+
/// assert_eq!(INIT.is_completed(), false);
179+
/// ```
180+
#[inline]
181+
#[unstable(feature = "nonpoison_once", issue = "134645")]
182+
pub fn is_completed(&self) -> bool {
183+
self.inner.is_completed()
184+
}
185+
186+
/// Blocks the current thread until initialization has completed.
187+
///
188+
/// # Example
189+
///
190+
/// ```rust
191+
/// use std::sync::Once;
192+
/// use std::thread;
193+
///
194+
/// static READY: Once = Once::new();
195+
///
196+
/// let thread = thread::spawn(|| {
197+
/// READY.wait();
198+
/// println!("everything is ready");
199+
/// });
200+
///
201+
/// READY.call_once(|| println!("performing setup"));
202+
/// ```
203+
///
204+
/// This function will continue to block even if a thread initializing via [`call_once()`] has
205+
/// panicked. This behavior is identical to the [`poison::Once::wait_force`] method.
206+
///
207+
/// [`call_once()`]: Once::call_once
208+
/// [`poison::Once::wait_force`]: crate::sync::poison::Once::wait_force
209+
#[unstable(feature = "nonpoison_once", issue = "134645")]
210+
pub fn wait(&self) {
211+
if !self.inner.is_completed() {
212+
self.inner.wait(true);
213+
}
214+
}
215+
216+
/// Returns the current state of the `Once` instance.
217+
///
218+
/// Since this takes a mutable reference, no initialization can currently
219+
/// be running, so the state must be either "incomplete", "poisoned" or
220+
/// "complete".
221+
#[inline]
222+
pub(crate) fn state(&mut self) -> ExclusiveState {
223+
self.inner.state()
224+
}
225+
226+
/// Sets current state of the `Once` instance.
227+
///
228+
/// Since this takes a mutable reference, no initialization can currently
229+
/// be running, so the state must be either "incomplete", "poisoned" or
230+
/// "complete".
231+
#[inline]
232+
pub(crate) fn set_state(&mut self, new_state: ExclusiveState) {
233+
self.inner.set_state(new_state);
234+
}
235+
}
236+
237+
#[unstable(feature = "nonpoison_once", issue = "134645")]
238+
impl fmt::Debug for Once {
239+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240+
f.debug_struct("Once").finish_non_exhaustive()
241+
}
242+
}

0 commit comments

Comments
 (0)