Skip to content

Commit a13a2d0

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 a13a2d0

File tree

2 files changed

+224
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)