/rust/registry/src/index.crates.io-1949cf8c6b5b557f/mea-0.6.4/src/latch/mod.rs
Line | Count | Source |
1 | | // Copyright 2024 tison <wander4096@gmail.com> |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | //! A countdown latch that allows one or more tasks to wait until a set of operations completes. |
16 | | //! |
17 | | //! Unlike a barrier, a latch's count can only decrease and cannot be reused once it reaches zero. |
18 | | //! This makes it ideal for scenarios where you need to wait for a specific number of events or |
19 | | //! operations to complete. |
20 | | //! |
21 | | //! A latch starts with an initial count and tasks can wait for this count to reach zero. |
22 | | //! The count can be decremented by calling [`count_down()`] or [`arrive()`]. Once the count |
23 | | //! reaches zero, all waiting tasks are unblocked. |
24 | | //! |
25 | | //! # Examples |
26 | | //! |
27 | | //! ``` |
28 | | //! # #[tokio::main] |
29 | | //! # async fn main() { |
30 | | //! use std::sync::Arc; |
31 | | //! |
32 | | //! use mea::latch::Latch; |
33 | | //! |
34 | | //! let latch = Arc::new(Latch::new(3)); |
35 | | //! let mut handles = Vec::new(); |
36 | | //! |
37 | | //! for i in 0..3 { |
38 | | //! let latch = latch.clone(); |
39 | | //! handles.push(tokio::spawn(async move { |
40 | | //! println!("Task {} starting", i); |
41 | | //! // Simulate some work |
42 | | //! latch.count_down(); // Signal completion |
43 | | //! })); |
44 | | //! } |
45 | | //! |
46 | | //! // Wait for all tasks to complete |
47 | | //! latch.wait().await; |
48 | | //! println!("All tasks completed"); |
49 | | //! # } |
50 | | //! ``` |
51 | | //! |
52 | | //! [`count_down()`]: Latch::count_down |
53 | | //! [`arrive()`]: Latch::arrive |
54 | | |
55 | | use std::fmt; |
56 | | use std::future::Future; |
57 | | use std::pin::Pin; |
58 | | use std::sync::Arc; |
59 | | use std::task::Context; |
60 | | use std::task::Poll; |
61 | | |
62 | | use crate::internal::CountdownState; |
63 | | |
64 | | #[cfg(test)] |
65 | | mod tests; |
66 | | |
67 | | /// A synchronization primitive that can be used to coordinate multiple tasks. |
68 | | /// |
69 | | /// See the [module level documentation](self) for more. |
70 | | #[derive(Debug)] |
71 | | pub struct Latch { |
72 | | state: CountdownState, |
73 | | } |
74 | | |
75 | | impl Latch { |
76 | | /// Creates a new latch initialized with the given count. |
77 | | /// |
78 | | /// # Arguments |
79 | | /// |
80 | | /// * `count` - The initial count value. Tasks will wait until this count reaches zero. |
81 | | /// |
82 | | /// # Examples |
83 | | /// |
84 | | /// ``` |
85 | | /// use mea::latch::Latch; |
86 | | /// |
87 | | /// let latch = Latch::new(3); // Creates a latch with count of 3 |
88 | | /// ``` |
89 | 0 | pub fn new(count: u32) -> Self { |
90 | 0 | Self { |
91 | 0 | state: CountdownState::new(count), |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | /// Returns the current count. |
96 | | /// |
97 | | /// This method is typically used for debugging and testing purposes. |
98 | | /// |
99 | | /// # Examples |
100 | | /// |
101 | | /// ``` |
102 | | /// use mea::latch::Latch; |
103 | | /// |
104 | | /// let latch = Latch::new(5); |
105 | | /// assert_eq!(latch.count(), 5); |
106 | | /// ``` |
107 | 0 | pub fn count(&self) -> u32 { |
108 | 0 | self.state.state() |
109 | 0 | } |
110 | | |
111 | | /// Decrements the latch count by one, waking up all pending tasks if the counter reaches zero. |
112 | | /// |
113 | | /// If the current count is zero, this method has no effect. |
114 | | /// |
115 | | /// # Examples |
116 | | /// |
117 | | /// ``` |
118 | | /// use mea::latch::Latch; |
119 | | /// |
120 | | /// let latch = Latch::new(2); |
121 | | /// latch.count_down(); // Count is now 1 |
122 | | /// latch.count_down(); // Count is now 0, all waiting tasks are woken |
123 | | /// ``` |
124 | 0 | pub fn count_down(&self) { |
125 | 0 | if self.state.decrement(1) { |
126 | 0 | self.state.wake_all(); |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | /// Decrements the latch count by `n`, waking up all waiting tasks if the counter reaches zero. |
131 | | /// |
132 | | /// This method provides a way to decrement the counter by more than one at a time. |
133 | | /// It will not cause an overflow when decrementing the counter. |
134 | | /// |
135 | | /// # Arguments |
136 | | /// |
137 | | /// * `n` - The amount to decrement the counter by |
138 | | /// |
139 | | /// # Behavior |
140 | | /// |
141 | | /// * If `n` is zero or the counter has already reached zero, nothing happens |
142 | | /// * If the current count is greater than `n`, it is decremented by `n` |
143 | | /// * If the current count is greater than 0 but less than or equal to `n`, the count becomes |
144 | | /// zero and all waiting tasks are woken |
145 | | /// |
146 | | /// # Examples |
147 | | /// |
148 | | /// ``` |
149 | | /// use mea::latch::Latch; |
150 | | /// |
151 | | /// let latch = Latch::new(5); |
152 | | /// latch.arrive(3); // Count is now 2 |
153 | | /// latch.arrive(2); // Count is now 0, all waiting tasks are woken |
154 | | /// ``` |
155 | 0 | pub fn arrive(&self, n: u32) { |
156 | 0 | if n != 0 && self.state.decrement(n) { |
157 | 0 | self.state.wake_all(); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | /// Attempts to wait for the latch count to reach zero without blocking. |
162 | | /// |
163 | | /// # Returns |
164 | | /// |
165 | | /// * `Ok(())` if the count is zero |
166 | | /// * `Err(count)` if the count is not zero, where `count` is the current count |
167 | | /// |
168 | | /// # Examples |
169 | | /// |
170 | | /// ``` |
171 | | /// use mea::latch::Latch; |
172 | | /// |
173 | | /// let latch = Latch::new(2); |
174 | | /// assert_eq!(latch.try_wait(), Err(2)); |
175 | | /// latch.count_down(); |
176 | | /// assert_eq!(latch.try_wait(), Err(1)); |
177 | | /// latch.count_down(); |
178 | | /// assert_eq!(latch.try_wait(), Ok(())); |
179 | | /// ``` |
180 | 0 | pub fn try_wait(&self) -> Result<(), u32> { |
181 | 0 | self.state.spin_wait(0) |
182 | 0 | } |
183 | | |
184 | | /// Returns a future that will complete when the latch count reaches zero. |
185 | | /// |
186 | | /// # Examples |
187 | | /// |
188 | | /// ``` |
189 | | /// # #[tokio::main] |
190 | | /// # async fn main() { |
191 | | /// use std::sync::Arc; |
192 | | /// |
193 | | /// use mea::latch::Latch; |
194 | | /// |
195 | | /// let latch = Arc::new(Latch::new(1)); |
196 | | /// let latch2 = latch.clone(); |
197 | | /// |
198 | | /// // Spawn a task that will wait for the latch |
199 | | /// let handle = tokio::spawn(async move { |
200 | | /// latch2.wait().await; |
201 | | /// println!("Latch reached zero!"); |
202 | | /// }); |
203 | | /// |
204 | | /// // Count down the latch |
205 | | /// latch.count_down(); |
206 | | /// handle.await.unwrap(); |
207 | | /// # } |
208 | | /// ``` |
209 | 0 | pub async fn wait(&self) { |
210 | 0 | let fut = LatchWait { |
211 | 0 | idx: None, |
212 | 0 | latch: self, |
213 | 0 | }; |
214 | 0 | fut.await |
215 | 0 | } |
216 | | |
217 | | /// Returns a future that will complete when the latch count reaches zero. |
218 | | /// |
219 | | /// The latch must be wrapped in an [`Arc`] to call this method. Thus, the returned future has |
220 | | /// no lifetime constraints. |
221 | | /// |
222 | | /// # Examples |
223 | | /// |
224 | | /// ``` |
225 | | /// # #[tokio::main] |
226 | | /// # async fn main() { |
227 | | /// use std::sync::Arc; |
228 | | /// |
229 | | /// use mea::latch::Latch; |
230 | | /// |
231 | | /// let latch = Arc::new(Latch::new(1)); |
232 | | /// let latch2 = latch.clone(); |
233 | | /// |
234 | | /// // Spawn a task that will wait for the latch |
235 | | /// let handle = tokio::spawn(async move { |
236 | | /// latch2.wait_owned().await; |
237 | | /// println!("Latch reached zero!"); |
238 | | /// }); |
239 | | /// |
240 | | /// // Count down the latch |
241 | | /// latch.count_down(); |
242 | | /// handle.await.unwrap(); |
243 | | /// # } |
244 | | /// ``` |
245 | 0 | pub async fn wait_owned(self: Arc<Self>) { |
246 | 0 | let fut = OwnedLatchWait { |
247 | 0 | idx: None, |
248 | 0 | latch: self, |
249 | 0 | }; |
250 | 0 | fut.await |
251 | 0 | } |
252 | | } |
253 | | |
254 | | impl Latch { |
255 | 0 | fn intern_poll(&self, idx: &mut Option<usize>, cx: &mut Context<'_>) -> Poll<()> { |
256 | | // register waker if the counter is not zero |
257 | 0 | if self.state.spin_wait(16).is_err() { |
258 | 0 | self.state.register_waker(idx, cx); |
259 | | // double check after register waker, to catch the update between two steps |
260 | 0 | if self.state.spin_wait(0).is_err() { |
261 | 0 | return Poll::Pending; |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | 0 | Poll::Ready(()) |
266 | 0 | } |
267 | | } |
268 | | |
269 | | /// A wait future returned by [`Latch::wait()`]. |
270 | | /// |
271 | | /// This future will complete when the latch count reaches zero. |
272 | | #[must_use = "futures do nothing unless you `.await` or poll them"] |
273 | | pub struct LatchWait<'a> { |
274 | | idx: Option<usize>, |
275 | | latch: &'a Latch, |
276 | | } |
277 | | |
278 | | impl fmt::Debug for LatchWait<'_> { |
279 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
280 | 0 | f.debug_struct("LatchWait").finish_non_exhaustive() |
281 | 0 | } |
282 | | } |
283 | | |
284 | | impl Future for LatchWait<'_> { |
285 | | type Output = (); |
286 | | |
287 | 0 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
288 | 0 | let Self { idx, latch } = self.get_mut(); |
289 | 0 | latch.intern_poll(idx, cx) |
290 | 0 | } |
291 | | } |
292 | | |
293 | | /// An owned wait future returned by [`Latch::wait()`]. |
294 | | /// |
295 | | /// This future will complete when the latch count reaches zero. |
296 | | #[must_use = "futures do nothing unless you `.await` or poll them"] |
297 | | pub struct OwnedLatchWait { |
298 | | idx: Option<usize>, |
299 | | latch: Arc<Latch>, |
300 | | } |
301 | | |
302 | | impl fmt::Debug for OwnedLatchWait { |
303 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
304 | 0 | f.debug_struct("OwnedLatchWait").finish_non_exhaustive() |
305 | 0 | } |
306 | | } |
307 | | |
308 | | impl Future for OwnedLatchWait { |
309 | | type Output = (); |
310 | | |
311 | 0 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
312 | 0 | let Self { idx, latch } = self.get_mut(); |
313 | 0 | latch.intern_poll(idx, cx) |
314 | 0 | } |
315 | | } |