Coverage Report

Created: 2026-07-16 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}