foyer_common/
countdown.rs1use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
16
17#[derive(Debug)]
19pub struct Countdown {
20 finish: AtomicBool,
21 counter: AtomicIsize,
22}
23
24impl Countdown {
25 pub fn new(counter: usize) -> Self {
31 Self {
32 finish: AtomicBool::new(false),
33 counter: AtomicIsize::new(isize::try_from(counter).expect("`counter` must NOT exceed `isize::MAX`.")),
34 }
35 }
36
37 pub fn countdown(&self) -> bool {
39 if self.finish.load(Ordering::Relaxed) {
40 return true;
41 }
42 self.counter.fetch_sub(1, Ordering::Relaxed) <= 0
43 }
44
45 pub fn reset(&self, counter: usize) {
47 self.finish.store(false, Ordering::Relaxed);
48 self.counter.store(
49 isize::try_from(counter).expect("`counter` must NOT exceed `isize::MAX`."),
50 Ordering::Relaxed,
51 );
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use std::time::Duration;
58
59 use futures_util::future::join_all;
60
61 use super::*;
62
63 async fn case(counter: usize, concurrency: usize) {
64 let cd = Countdown::new(counter);
65 let res = join_all((0..concurrency).map(|_| async {
66 tokio::time::sleep(Duration::from_millis(10)).await;
67 cd.countdown()
68 }))
69 .await;
70 assert_eq!(counter, res.into_iter().filter(|b| !b).count());
71 }
72
73 #[tokio::test]
74 async fn test_countdown() {
75 for counter in [1, 4, 8, 16] {
76 for concurrency in [16, 32, 64, 128] {
77 case(counter, concurrency).await;
78 }
79 }
80 }
81}