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}