spin_sync/once.rs
1// Copyright 2020 Shin Yoshida
2//
3// "LGPL-3.0-or-later OR Apache-2.0 OR BSD-2-Clause"
4//
5// This is part of spin-sync
6//
7// spin-sync is free software: you can redistribute it and/or modify
8// it under the terms of the GNU Lesser General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// spin-sync is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU Lesser General Public License for more details.
16//
17// You should have received a copy of the GNU Lesser General Public License
18// along with spin-sync. If not, see <http://www.gnu.org/licenses/>.
19//
20//
21// Licensed under the Apache License, Version 2.0 (the "License");
22// you may not use this file except in compliance with the License.
23// You may obtain a copy of the License at
24//
25// http://www.apache.org/licenses/LICENSE-2.0
26//
27// Unless required by applicable law or agreed to in writing, software
28// distributed under the License is distributed on an "AS IS" BASIS,
29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30// See the License for the specific language governing permissions and
31// limitations under the License.
32//
33//
34// Redistribution and use in source and binary forms, with or without modification, are permitted
35// provided that the following conditions are met:
36//
37// 1. Redistributions of source code must retain the above copyright notice, this list of
38// conditions and the following disclaimer.
39// 2. Redistributions in binary form must reproduce the above copyright notice, this
40// list of conditions and the following disclaimer in the documentation and/or other
41// materials provided with the distribution.
42//
43// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
44// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
45// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
46// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
47// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
50// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52// POSSIBILITY OF SUCH DAMAGE.
53
54use crate::misc::PhantomOnce;
55use std::fmt;
56use std::sync::atomic::{AtomicU8, Ordering};
57
58/// A synchronization primitive which can be used to run a one-time global initialization.
59///
60/// `Once` behaves like `std::sync::Once` except for using spinlock.
61/// Useful for one-time initialization for FFI or related functionality.
62///
63/// # Examples
64///
65/// ```
66/// use spin_sync::Once;
67///
68/// static INIT: Once = Once::new();
69///
70/// INIT.call_once(|| {
71/// // Do some initialization here.
72/// });
73/// ```
74pub struct Once {
75 state: AtomicU8,
76 _phantom: PhantomOnce,
77}
78
79impl fmt::Debug for Once {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 f.pad("Once { .. }")
82 }
83}
84
85impl Once {
86 /// Create a new `Once` instance.
87 pub const fn new() -> Self {
88 Self {
89 state: AtomicU8::new(OnceState::default().state),
90 _phantom: PhantomOnce {},
91 }
92 }
93
94 /// Performs an initialization routine once and only once. The given closure will be executed
95 /// if this is the first time [`call_once`] has been called, and otherwise the routine will not be invoked.
96 ///
97 /// This method will block the calling thread if another initialization routine is currently running.
98 ///
99 /// When this function returns, it is guaranteed that some initialization has run and completed
100 /// (it may not be the closure specified). It is also guaranteed that any memory writes performed
101 /// by the executed closure can be reliably observed by other threads at this point (there is a happens-before
102 /// relation between the closure and code executing after the return).
103 ///
104 /// If the given closure recursively invokes [`call_once`] on the same `Once` instance the exact behavior
105 /// is not specified, allowed outcomes are a panic or a deadlock.
106 ///
107 /// [`call_once`]: #method.call_once
108 ///
109 /// # Examples
110 ///
111 /// `Once` enable to access static mut data safely.
112 ///
113 /// ```
114 /// use spin_sync::Once;
115 ///
116 /// static mut CACHE: usize = 0;
117 /// static INIT: Once = Once::new();
118 ///
119 /// fn expensive_calculation(val: usize) -> usize {
120 /// unsafe {
121 /// INIT.call_once(|| { CACHE = val; });
122 /// CACHE
123 /// }
124 /// }
125 ///
126 /// // INIT.call_once() invokes the closure and set the CACHE.
127 /// assert_eq!(1, expensive_calculation(1));
128 ///
129 /// // INIT.call_once() do nothing and return the CACHE.
130 /// assert_eq!(1, expensive_calculation(2));
131 /// ```
132 ///
133 /// # Panics
134 ///
135 /// The closure f will only be executed once if this is called concurrently among
136 /// many threads. If that closure panics, however, then it will poison this `Once` instance,
137 /// causing all future invocations of [`call_once`] to also panic.
138 ///
139 /// [`call_once`]: #method.call_once
140 pub fn call_once<F: FnOnce()>(&self, f: F) {
141 let (_guard, s) = self.lock();
142
143 if s.poisoned() {
144 panic!("`Once.call_once()' is called while the instance is poisoned.");
145 }
146
147 if s.finished() {
148 return;
149 }
150
151 f();
152 let s = s.finish();
153 self.state.store(s.state, Ordering::Relaxed);
154 }
155
156 /// Performs the same function as [`call_once`] except ignores poisoning.
157 ///
158 /// Unlike [`call_once`], if this `Once` has been poisoned (i.e., a previous call to [`call_once`]
159 /// or [`call_once_force`] caused a panic), calling [`call_once_force`] will still invoke the closure
160 /// f and will not result in an immediate panic. If f panics, the `Once` will remain in a poison state.
161 /// If f does not panic, the `Once` will no longer be in a poison state and all future calls to
162 /// [`call_once`] or [`call_once_force`] will be no-ops.
163 ///
164 ///
165 /// The closure f is yielded a [`OnceState`] structure which can be used to query the poison status of the `Once`.
166 ///
167 /// [`call_once`]: #method.call_once
168 /// [`call_once_force`]: #method.call_once_force
169 /// [`OnceState`]: struct.OnceState.html
170 ///
171 /// # Examples
172 ///
173 /// ```
174 /// use spin_sync::Once;
175 /// use std::thread;
176 ///
177 /// static INIT: Once = Once::new();
178 ///
179 /// // Poison INIT
180 /// let handle = thread::spawn(|| {
181 /// INIT.call_once(|| panic!());
182 /// });
183 /// assert!(handle.join().is_err());
184 ///
185 /// // Poisoning propagates
186 /// let handle = thread::spawn(|| {
187 /// INIT.call_once(|| {});
188 /// });
189 /// assert!(handle.join().is_err());
190 ///
191 /// // call_once_force will still run and reset the poisoned state
192 /// INIT.call_once_force(|state| {
193 /// assert!(state.poisoned());
194 /// });
195 ///
196 /// // once any success happens, we stop propagating the poison
197 /// INIT.call_once(|| {});
198 /// ```
199 pub fn call_once_force<F: FnOnce(&OnceState)>(&self, f: F) {
200 let (_guard, s) = self.lock();
201
202 if s.finished() {
203 return;
204 }
205
206 f(&s);
207 let s = s.finish();
208 let s = s.unpoison();
209 self.state.store(s.state, Ordering::Relaxed);
210 }
211
212 /// Returns true if some [`call_once`] call has completed successfully.
213 /// Specifically, [`is_completed`] will return false in the following situations:
214 ///
215 /// * Neither [`call_once`] nor [`call_once_force`] was not called at all,
216 /// * [`call_once`] or/and [`call_once_force`] was called, but has not yet completed,
217 /// * the `Once` instance is poisoned
218 ///
219 /// This function returning false does not mean that `Once` has not been executed.
220 /// For example, it may have been executed in the time between when [`is_completed`]
221 /// starts executing and when it returns, in which case the false return value would
222 /// be stale (but still permissible).
223 ///
224 /// [`call_once`]: #method.call_once
225 /// [`call_once_force`]: #method.call_once_force
226 /// [`is_completed`]: #method.is_completed
227 ///
228 /// # Examples
229 ///
230 /// `call_once` was succeeded.
231 ///
232 /// ```
233 /// use spin_sync::Once;
234 ///
235 /// static INIT: Once = Once::new();
236 ///
237 /// assert_eq!(INIT.is_completed(), false);
238 /// INIT.call_once(|| {
239 /// assert_eq!(INIT.is_completed(), false);
240 /// });
241 /// assert_eq!(INIT.is_completed(), true);
242 /// ```
243 ///
244 /// `call_once` caused panic.
245 ///
246 /// ```
247 /// use spin_sync::Once;
248 /// use std::thread;
249 ///
250 /// static INIT: Once = Once::new();
251 ///
252 /// assert_eq!(INIT.is_completed(), false);
253 /// let handle = thread::spawn(|| {
254 /// INIT.call_once(|| panic!());
255 /// });
256 /// assert!(handle.join().is_err());
257 /// assert_eq!(INIT.is_completed(), false);
258 /// ```
259 #[must_use]
260 pub fn is_completed(&self) -> bool {
261 let s = OnceState::new(self.state.load(Ordering::Relaxed));
262 (!s.poisoned()) && (s.finished())
263 }
264
265 fn lock(&self) -> (OnceGuard, OnceState) {
266 let mut expected = OnceState::default();
267 loop {
268 let desired = expected.acquire_lock();
269
270 let current = OnceState::new(self.state.compare_and_swap(
271 expected.state,
272 desired.state,
273 Ordering::Acquire,
274 ));
275
276 // self is locked now. Try again later.
277 if current.locked() {
278 expected = current.release_lock();
279 std::thread::yield_now();
280 continue;
281 }
282
283 // Succeed
284 if current.state == expected.state {
285 return (OnceGuard { once: &self }, desired);
286 }
287
288 // expected was wrong.
289 expected = current;
290 }
291 }
292}
293
294struct OnceGuard<'a> {
295 once: &'a Once,
296}
297
298impl Drop for OnceGuard<'_> {
299 fn drop(&mut self) {
300 let mut s = OnceState::new(self.once.state.load(Ordering::Relaxed));
301 debug_assert!(s.locked());
302
303 if std::thread::panicking() {
304 s = s.poison();
305 }
306
307 s = s.release_lock();
308 self.once.state.store(s.state, Ordering::Release);
309 }
310}
311
312/// State yielded to [`call_once_force`] ’s closure parameter. The state can be used to query
313/// the poison status of the [`Once`]
314///
315/// [`call_once_force`]: struct.Once.html#method.call_once_force
316/// [`Once`]: struct.Once.html
317#[derive(Debug)]
318pub struct OnceState {
319 state: u8,
320}
321
322impl OnceState {
323 const INIT: u8 = 0;
324 const LOCK: u8 = 1;
325 const FINISHED: u8 = 2;
326 const POISONED: u8 = 4;
327
328 #[must_use]
329 const fn default() -> Self {
330 Self { state: Self::INIT }
331 }
332
333 #[must_use]
334 const fn new(state: u8) -> Self {
335 Self { state }
336 }
337
338 #[must_use]
339 const fn locked(&self) -> bool {
340 (self.state & Self::LOCK) != 0
341 }
342
343 #[must_use]
344 const fn finished(&self) -> bool {
345 (self.state & Self::FINISHED) != 0
346 }
347
348 #[must_use]
349 /// Returns true if the associated [`Once`] was poisoned prior to the invocation of the closure
350 /// passed to [`call_once_force`] .
351 ///
352 /// [`Once`]: struct.Once.html
353 /// [`call_once_force`]: struct.Once.html#method.call_once_force
354 pub const fn poisoned(&self) -> bool {
355 (self.state & Self::POISONED) != 0
356 }
357
358 #[must_use]
359 fn acquire_lock(&self) -> Self {
360 debug_assert!(!self.locked());
361 Self::new(self.state | Self::LOCK)
362 }
363
364 #[must_use]
365 fn release_lock(&self) -> Self {
366 debug_assert!(self.locked());
367 Self::new(self.state ^ Self::LOCK)
368 }
369
370 #[must_use]
371 fn finish(&self) -> Self {
372 debug_assert!(!self.finished());
373 Self::new(self.state | Self::FINISHED)
374 }
375
376 #[must_use]
377 fn poison(&self) -> Self {
378 Self::new(self.state | Self::POISONED)
379 }
380
381 #[must_use]
382 fn unpoison(&self) -> Self {
383 Self::new(self.state ^ Self::POISONED)
384 }
385}
386
387#[cfg(test)]
388mod tests {
389 use super::*;
390
391 #[test]
392 fn call_once_invoke_task_only_once() {
393 let mut val = 0;
394 let once = Once::new();
395
396 assert_eq!(0, val);
397
398 once.call_once(|| val = 1);
399 assert_eq!(1, val);
400
401 once.call_once(|| val = 2);
402 assert_eq!(1, val);
403 }
404
405 #[test]
406 fn call_once_force_do_nothing_after_call_once_succeeded() {
407 let mut val = 0;
408 let once = Once::new();
409
410 assert_eq!(0, val);
411
412 once.call_once(|| val = 1);
413 assert_eq!(1, val);
414
415 once.call_once_force(|_| val = 2);
416 assert_eq!(1, val);
417 }
418}