xref: /DragonOS/kernel/src/libs/once.rs (revision 863a3cff06e618a5f0fc03920dfd5732452344c9)
1*863a3cffSLoGin use core::{
2*863a3cffSLoGin     fmt::{self, Debug, Formatter},
3*863a3cffSLoGin     sync::atomic::Ordering,
4*863a3cffSLoGin };
5*863a3cffSLoGin 
6*863a3cffSLoGin use atomic_enum::atomic_enum;
7*863a3cffSLoGin 
8*863a3cffSLoGin pub struct Once {
9*863a3cffSLoGin     inner: AtomicOnceState,
10*863a3cffSLoGin }
11*863a3cffSLoGin 
12*863a3cffSLoGin #[atomic_enum]
13*863a3cffSLoGin #[derive(PartialEq, Eq)]
14*863a3cffSLoGin pub enum OnceState {
15*863a3cffSLoGin     Incomplete,
16*863a3cffSLoGin     Posioned,
17*863a3cffSLoGin     Complete,
18*863a3cffSLoGin }
19*863a3cffSLoGin 
20*863a3cffSLoGin #[allow(dead_code)]
21*863a3cffSLoGin impl Once {
new() -> Self22*863a3cffSLoGin     pub const fn new() -> Self {
23*863a3cffSLoGin         Self {
24*863a3cffSLoGin             inner: AtomicOnceState::new(OnceState::Incomplete),
25*863a3cffSLoGin         }
26*863a3cffSLoGin     }
27*863a3cffSLoGin 
28*863a3cffSLoGin     #[track_caller]
call_once<F: FnOnce()>(&self, f: F)29*863a3cffSLoGin     pub fn call_once<F: FnOnce()>(&self, f: F) {
30*863a3cffSLoGin         if self.is_completed() {
31*863a3cffSLoGin             return;
32*863a3cffSLoGin         }
33*863a3cffSLoGin 
34*863a3cffSLoGin         // set initialized
35*863a3cffSLoGin         let r = self.inner.compare_exchange(
36*863a3cffSLoGin             OnceState::Incomplete,
37*863a3cffSLoGin             OnceState::Posioned,
38*863a3cffSLoGin             Ordering::SeqCst,
39*863a3cffSLoGin             Ordering::SeqCst,
40*863a3cffSLoGin         );
41*863a3cffSLoGin         if r.is_err() {
42*863a3cffSLoGin             return;
43*863a3cffSLoGin         }
44*863a3cffSLoGin         // call function
45*863a3cffSLoGin         f();
46*863a3cffSLoGin         // set completed
47*863a3cffSLoGin         self.inner.store(OnceState::Complete, Ordering::SeqCst);
48*863a3cffSLoGin     }
49*863a3cffSLoGin 
50*863a3cffSLoGin     /// Performs the same function as [`call_once()`] except ignores poisoning.
51*863a3cffSLoGin     ///
52*863a3cffSLoGin     /// Unlike [`call_once()`], if this [`Once`] has been poisoned (i.e., a previous
53*863a3cffSLoGin     /// call to [`call_once()`] or [`call_once_force()`] caused a panic), calling
54*863a3cffSLoGin     /// [`call_once_force()`] will still invoke the closure `f` and will _not_
55*863a3cffSLoGin     /// result in an immediate panic. If `f` panics, the [`Once`] will remain
56*863a3cffSLoGin     /// in a poison state. If `f` does _not_ panic, the [`Once`] will no
57*863a3cffSLoGin     /// longer be in a poison state and all future calls to [`call_once()`] or
58*863a3cffSLoGin     /// [`call_once_force()`] will be no-ops.
59*863a3cffSLoGin     ///
60*863a3cffSLoGin     /// The closure `f` is yielded a [`OnceState`] structure which can be used
61*863a3cffSLoGin     /// to query the poison status of the [`Once`].
62*863a3cffSLoGin     ///
63*863a3cffSLoGin     /// [`call_once()`]: Once::call_once
64*863a3cffSLoGin     /// [`call_once_force()`]: Once::call_once_force
65*863a3cffSLoGin     ///
66*863a3cffSLoGin     /// # Examples
67*863a3cffSLoGin     ///
68*863a3cffSLoGin     /// ```
69*863a3cffSLoGin     /// use std::sync::Once;
70*863a3cffSLoGin     /// use std::thread;
71*863a3cffSLoGin     ///
72*863a3cffSLoGin     /// static INIT: Once = Once::new();
73*863a3cffSLoGin     ///
74*863a3cffSLoGin     /// // poison the once
75*863a3cffSLoGin     /// let handle = thread::spawn(|| {
76*863a3cffSLoGin     ///     INIT.call_once(|| panic!());
77*863a3cffSLoGin     /// });
78*863a3cffSLoGin     /// assert!(handle.join().is_err());
79*863a3cffSLoGin     ///
80*863a3cffSLoGin     /// // poisoning propagates
81*863a3cffSLoGin     /// let handle = thread::spawn(|| {
82*863a3cffSLoGin     ///     INIT.call_once(|| {});
83*863a3cffSLoGin     /// });
84*863a3cffSLoGin     /// assert!(handle.join().is_err());
85*863a3cffSLoGin     ///
86*863a3cffSLoGin     /// // call_once_force will still run and reset the poisoned state
87*863a3cffSLoGin     /// INIT.call_once_force(|state| {
88*863a3cffSLoGin     ///     assert!(state.is_poisoned());
89*863a3cffSLoGin     /// });
90*863a3cffSLoGin     ///
91*863a3cffSLoGin     /// // once any success happens, we stop propagating the poison
92*863a3cffSLoGin     /// INIT.call_once(|| {});
93*863a3cffSLoGin     /// ```
call_once_force<F>(&self, f: F) where F: FnOnce(&OnceState),94*863a3cffSLoGin     pub fn call_once_force<F>(&self, f: F)
95*863a3cffSLoGin     where
96*863a3cffSLoGin         F: FnOnce(&OnceState),
97*863a3cffSLoGin     {
98*863a3cffSLoGin         // fast path check
99*863a3cffSLoGin         if self.is_completed() {
100*863a3cffSLoGin             return;
101*863a3cffSLoGin         }
102*863a3cffSLoGin 
103*863a3cffSLoGin         // set poisoned
104*863a3cffSLoGin         self.inner
105*863a3cffSLoGin             .compare_exchange(
106*863a3cffSLoGin                 OnceState::Incomplete,
107*863a3cffSLoGin                 OnceState::Posioned,
108*863a3cffSLoGin                 Ordering::SeqCst,
109*863a3cffSLoGin                 Ordering::SeqCst,
110*863a3cffSLoGin             )
111*863a3cffSLoGin             .ok();
112*863a3cffSLoGin 
113*863a3cffSLoGin         // call function
114*863a3cffSLoGin         f(&self.inner.load(Ordering::SeqCst));
115*863a3cffSLoGin 
116*863a3cffSLoGin         // set initialized
117*863a3cffSLoGin         self.inner.store(OnceState::Complete, Ordering::SeqCst);
118*863a3cffSLoGin     }
119*863a3cffSLoGin 
120*863a3cffSLoGin     /// Fast path check
121*863a3cffSLoGin     #[inline]
is_completed(&self) -> bool122*863a3cffSLoGin     pub fn is_completed(&self) -> bool {
123*863a3cffSLoGin         self.inner.load(Ordering::SeqCst) == OnceState::Complete
124*863a3cffSLoGin     }
125*863a3cffSLoGin 
126*863a3cffSLoGin     /// Returns the current state of the `Once` instance.
127*863a3cffSLoGin     #[inline]
state(&self) -> OnceState128*863a3cffSLoGin     pub fn state(&self) -> OnceState {
129*863a3cffSLoGin         self.inner.load(Ordering::SeqCst)
130*863a3cffSLoGin     }
131*863a3cffSLoGin }
132*863a3cffSLoGin 
133*863a3cffSLoGin impl Debug for Once {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result134*863a3cffSLoGin     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
135*863a3cffSLoGin         f.debug_struct("Once").finish_non_exhaustive()
136*863a3cffSLoGin     }
137*863a3cffSLoGin }
138*863a3cffSLoGin 
139*863a3cffSLoGin #[allow(dead_code)]
140*863a3cffSLoGin impl OnceState {
141*863a3cffSLoGin     /// Returns `true` if the associated [`Once`] was poisoned prior to the
142*863a3cffSLoGin     /// invocation of the closure passed to [`Once::call_once_force()`].
143*863a3cffSLoGin     ///
144*863a3cffSLoGin     /// # Examples
145*863a3cffSLoGin     ///
146*863a3cffSLoGin     /// A poisoned [`Once`]:
147*863a3cffSLoGin     ///
148*863a3cffSLoGin     /// ```
149*863a3cffSLoGin     /// use std::sync::Once;
150*863a3cffSLoGin     /// use std::thread;
151*863a3cffSLoGin     ///
152*863a3cffSLoGin     /// static INIT: Once = Once::new();
153*863a3cffSLoGin     ///
154*863a3cffSLoGin     /// // poison the once
155*863a3cffSLoGin     /// let handle = thread::spawn(|| {
156*863a3cffSLoGin     ///     INIT.call_once(|| panic!());
157*863a3cffSLoGin     /// });
158*863a3cffSLoGin     /// assert!(handle.join().is_err());
159*863a3cffSLoGin     ///
160*863a3cffSLoGin     /// INIT.call_once_force(|state| {
161*863a3cffSLoGin     ///     assert!(state.is_poisoned());
162*863a3cffSLoGin     /// });
163*863a3cffSLoGin     /// ```
164*863a3cffSLoGin     ///
165*863a3cffSLoGin     /// An unpoisoned [`Once`]:
166*863a3cffSLoGin     ///
167*863a3cffSLoGin     /// ```
168*863a3cffSLoGin     /// use std::sync::Once;
169*863a3cffSLoGin     ///
170*863a3cffSLoGin     /// static INIT: Once = Once::new();
171*863a3cffSLoGin     ///
172*863a3cffSLoGin     /// INIT.call_once_force(|state| {
173*863a3cffSLoGin     ///     assert!(!state.is_poisoned());
174*863a3cffSLoGin     /// });
175*863a3cffSLoGin     #[inline]
is_poisoned(&self) -> bool176*863a3cffSLoGin     pub fn is_poisoned(&self) -> bool {
177*863a3cffSLoGin         *self == OnceState::Posioned
178*863a3cffSLoGin     }
179*863a3cffSLoGin }
180