/rust/registry/src/index.crates.io-1949cf8c6b5b557f/conquer-once-0.3.2/src/state.rs
Line | Count | Source |
1 | | use core::convert::{TryFrom, TryInto}; |
2 | | use core::sync::atomic::{AtomicUsize, Ordering}; |
3 | | |
4 | | #[cfg(feature = "std")] |
5 | | use crate::park::StackWaiter; |
6 | | use crate::POISON_PANIC_MSG; |
7 | | |
8 | | use self::OnceState::{Ready, Uninit, WouldBlock}; |
9 | | |
10 | | const WOULD_BLOCK: usize = 0; |
11 | | const UNINIT: usize = 1; |
12 | | const READY: usize = 2; |
13 | | const POISONED: usize = 3; |
14 | | |
15 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
16 | | // AtomicState (public but not exported) |
17 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
18 | | |
19 | | /// The concurrently and atomically mutable internal state of a [`OnceCell`]. |
20 | | /// |
21 | | /// A `WOULD_BLOCK` value is also interpreted as a `null` pointer to an empty |
22 | | /// [`WaiterQueue`] indicating that there is only a single blocked thread. |
23 | | #[derive(Debug)] |
24 | | pub struct AtomicOnceState(AtomicUsize); |
25 | | |
26 | | /********** impl inherent *************************************************************************/ |
27 | | |
28 | | impl AtomicOnceState { |
29 | | /// Creates a new `UNINIT` state. |
30 | | #[inline] |
31 | | pub(crate) const fn new() -> Self { |
32 | | Self(AtomicUsize::new(UNINIT)) |
33 | | } |
34 | | |
35 | | /// Creates a new `READY` state. |
36 | | #[inline] |
37 | | pub(crate) const fn ready() -> Self { |
38 | | Self(AtomicUsize::new(READY)) |
39 | | } |
40 | | |
41 | | /// Loads the current state using `ordering`. |
42 | | /// |
43 | | /// A Poisoning of the state is returned as a [`PoisonError`]. |
44 | | #[inline] |
45 | 0 | pub(crate) fn load(&self, order: Ordering) -> Result<OnceState, PoisonError> { |
46 | 0 | self.0.load(order).try_into() |
47 | 0 | } |
48 | | |
49 | | /// Attempts to set the state to blocked and fails if the state is either |
50 | | /// already initialized or blocked. |
51 | | #[inline] |
52 | 0 | pub(crate) fn try_block(&self, order: Ordering) -> Result<(), TryBlockError> { |
53 | 0 | let prev = match self.0.compare_exchange(UNINIT, WOULD_BLOCK, order, Ordering::Relaxed) { |
54 | 0 | Ok(prev) => prev, |
55 | 0 | Err(prev) => prev, |
56 | | }; |
57 | | |
58 | 0 | match prev.try_into().expect(POISON_PANIC_MSG) { |
59 | 0 | Uninit => Ok(()), |
60 | 0 | Ready => Err(TryBlockError::AlreadyInit), |
61 | 0 | WouldBlock(state) => Err(TryBlockError::WouldBlock(state)), |
62 | | } |
63 | 0 | } |
64 | | |
65 | | /// Unblocks the state by replacing it with either `Ready` or `Poisoned`. |
66 | | /// |
67 | | /// # Safety |
68 | | /// |
69 | | /// Must not be called if unblocking might cause data races due to |
70 | | /// un-synchronized reads and/or writes. |
71 | | #[inline] |
72 | 0 | pub(crate) unsafe fn unblock(&self, state: SwapState, order: Ordering) -> BlockedState { |
73 | 0 | BlockedState(self.0.swap(state as usize, order)) |
74 | 0 | } |
75 | | |
76 | | #[cfg(feature = "std")] |
77 | | /// Attempts to compare-and-swap the head of the `current` [`WaiterQueue]` |
78 | | /// with the `next` queue. |
79 | | #[inline] |
80 | | pub(crate) unsafe fn try_swap_blocked( |
81 | | &self, |
82 | | current: BlockedState, |
83 | | new: BlockedState, |
84 | | success: Ordering, |
85 | | ) -> Result<(), OnceState> { |
86 | | let prev = |
87 | | match self.0.compare_exchange(current.into(), new.into(), success, Ordering::Relaxed) { |
88 | | Ok(prev) => prev, |
89 | | Err(prev) => prev, |
90 | | }; |
91 | | |
92 | | match prev { |
93 | | prev if prev == current.into() => Ok(()), |
94 | | prev => Err(prev.try_into().expect(POISON_PANIC_MSG)), |
95 | | } |
96 | | } |
97 | | } |
98 | | |
99 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
100 | | // OnceState |
101 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
102 | | |
103 | | #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] |
104 | | pub(crate) enum OnceState { |
105 | | /// Initial (uninitialized) state. |
106 | | Uninit, |
107 | | /// Ready (initialized) state. |
108 | | Ready, |
109 | | /// Blocked state with queue of waiting threads. |
110 | | WouldBlock(BlockedState), |
111 | | } |
112 | | |
113 | | /********** impl TryFrom **************************************************************************/ |
114 | | |
115 | | impl TryFrom<usize> for OnceState { |
116 | | type Error = PoisonError; |
117 | | |
118 | | #[inline] |
119 | 0 | fn try_from(value: usize) -> Result<Self, Self::Error> { |
120 | 0 | match value { |
121 | 0 | POISONED => Err(PoisonError), |
122 | 0 | UNINIT => Ok(Uninit), |
123 | 0 | READY => Ok(Ready), |
124 | 0 | state => Ok(WouldBlock(BlockedState(state))), |
125 | | } |
126 | 0 | } |
127 | | } |
128 | | |
129 | | /********** impl From (usize) *********************************************************************/ |
130 | | |
131 | | impl From<OnceState> for usize { |
132 | | #[inline] |
133 | | fn from(state: OnceState) -> Self { |
134 | | match state { |
135 | | OnceState::Ready => READY, |
136 | | OnceState::Uninit => UNINIT, |
137 | | OnceState::WouldBlock(BlockedState(state)) => state, |
138 | | } |
139 | | } |
140 | | } |
141 | | |
142 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
143 | | // TryBlockError |
144 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
145 | | |
146 | | #[derive(Debug)] |
147 | | pub enum TryBlockError { |
148 | | AlreadyInit, |
149 | | WouldBlock(BlockedState), |
150 | | } |
151 | | |
152 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
153 | | // PoisonError |
154 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
155 | | |
156 | | /// An error type indicating a `OnceCell` has been poisoned. |
157 | | #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] |
158 | | pub struct PoisonError; |
159 | | |
160 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
161 | | // BlockedState (public but not exported) |
162 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
163 | | |
164 | | #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] |
165 | | pub struct BlockedState(usize); |
166 | | |
167 | | /********** impl inherent *************************************************************************/ |
168 | | |
169 | | impl BlockedState { |
170 | | #[cfg(feature = "std")] |
171 | | pub(crate) fn as_ptr(self) -> *const () { |
172 | | self.0 as *const _ |
173 | | } |
174 | | } |
175 | | |
176 | | /********** impl From (for usize) *****************************************************************/ |
177 | | |
178 | | impl From<BlockedState> for usize { |
179 | | #[inline] |
180 | | fn from(state: BlockedState) -> Self { |
181 | | state.0 |
182 | | } |
183 | | } |
184 | | |
185 | | /********** impl From (*const StackWaiter) ********************************************************/ |
186 | | |
187 | | #[cfg(feature = "std")] |
188 | | impl From<*const StackWaiter> for BlockedState { |
189 | | #[inline] |
190 | | fn from(waiter: *const StackWaiter) -> Self { |
191 | | Self(waiter as usize) |
192 | | } |
193 | | } |
194 | | |
195 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
196 | | // SwapState |
197 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
198 | | |
199 | | #[repr(usize)] |
200 | | pub(crate) enum SwapState { |
201 | | Ready = READY, |
202 | | Poisoned = POISONED, |
203 | | } |