tuwunel_core/utils/
two_phase_counter.rs1use std::{
4 collections::VecDeque,
5 ops::{Deref, Range},
6 sync::{Arc, RwLock},
7};
8
9use crate::{Result, checked, is_equal_to};
10
11pub struct Counter<F: Fn(u64) -> Result + Send + Sync> {
26 inner: RwLock<State<F>>,
28}
29
30pub struct State<F: Fn(u64) -> Result + Send + Sync> {
32 dispatched: u64,
35
36 commit: F,
39
40 pending: VecDeque<u64>,
44
45 release: F,
48}
49
50#[clippy::has_significant_drop]
51pub struct Permit<F: Fn(u64) -> Result + Send + Sync> {
57 state: Arc<Counter<F>>,
59
60 retired: u64,
63
64 id: u64,
66}
67
68impl<F: Fn(u64) -> Result + Send + Sync> Counter<F> {
69 pub fn new(init: u64, commit: F, release: F) -> Arc<Self> {
73 Arc::new(Self {
74 inner: State::new(init, commit, release).into(),
75 })
76 }
77
78 pub fn next(self: &Arc<Self>) -> Result<Permit<F>> {
80 let (retired, id) = self.inner.write()?.dispatch()?;
81
82 Ok(Permit::<F> { state: self.clone(), retired, id })
83 }
84
85 #[inline]
87 pub fn range(&self) -> Range<u64> {
88 let inner = self.inner.read().expect("locked for reading");
89
90 Range {
91 start: inner.retired(),
92 end: inner.dispatched,
93 }
94 }
95
96 #[inline]
99 pub fn current(&self) -> u64 {
100 self.inner
101 .read()
102 .expect("locked for reading")
103 .retired()
104 }
105
106 #[inline]
109 pub fn dispatched(&self) -> u64 {
110 self.inner
111 .read()
112 .expect("locked for reading")
113 .dispatched
114 }
115}
116
117impl<F: Fn(u64) -> Result + Send + Sync> State<F> {
118 fn new(dispatched: u64, commit: F, release: F) -> Self {
121 Self {
122 dispatched,
123 commit,
124 pending: VecDeque::new(),
125 release,
126 }
127 }
128
129 fn dispatch(&mut self) -> Result<(u64, u64)> {
132 let prev = self.dispatched;
133 let retired = self.retired();
134 let dispatched = checked!(prev + 1)?;
135 debug_assert!(
136 !self.check_pending(dispatched),
137 "sequence number cannot already be pending",
138 );
139
140 (self.commit)(dispatched)?;
141 self.dispatched = dispatched;
142 self.pending.push_back(self.dispatched);
143 Ok((retired, self.dispatched))
144 }
145
146 fn retire(&mut self, id: u64) {
148 debug_assert!(self.check_pending(id), "sequence number must be currently pending");
149
150 let index = self
151 .pending_index(id)
152 .expect("sequence number must be found as pending");
153
154 let removed = self
155 .pending
156 .remove(index)
157 .expect("sequence number at index must be removed");
158
159 debug_assert_eq!(removed, id, "sequence number removed must match id");
160
161 if index != 0 {
163 return;
164 }
165
166 let release = if self.pending.is_empty() { self.dispatched } else { id };
168
169 debug_assert!(release >= id, "sequence number released must not be less than id");
170
171 (self.release)(release).expect("release callback should not error");
172 }
173
174 fn retired(&self) -> u64 {
178 debug_assert!(
179 self.pending.iter().is_sorted(),
180 "Pending values should be naturally sorted"
181 );
182
183 self.pending
184 .front()
185 .map(|val| val.saturating_sub(1))
186 .unwrap_or(self.dispatched)
187 }
188
189 fn pending_index(&self, id: u64) -> Option<usize> {
191 debug_assert!(
192 self.pending.iter().is_sorted(),
193 "Pending values should be naturally sorted"
194 );
195
196 self.pending.binary_search(&id).ok()
197 }
198
199 fn check_pending(&self, id: u64) -> bool { self.pending.iter().any(is_equal_to!(&id)) }
202}
203
204impl<F: Fn(u64) -> Result + Send + Sync> Permit<F> {
205 #[inline]
208 #[must_use]
209 pub fn retired(&self) -> &u64 { &self.retired }
210
211 #[inline]
213 #[must_use]
214 pub fn id(&self) -> &u64 { &self.id }
215}
216
217impl<F: Fn(u64) -> Result + Send + Sync> Deref for Permit<F> {
218 type Target = u64;
219
220 #[inline]
221 fn deref(&self) -> &Self::Target { self.id() }
222}
223
224impl<F: Fn(u64) -> Result + Send + Sync> Drop for Permit<F> {
225 fn drop(&mut self) {
226 self.state
227 .inner
228 .write()
229 .expect("locked for writing")
230 .retire(self.id);
231 }
232}