ntex_util/services/
counter.rs1use std::{
2 cell::Cell, cell::OnceCell, cell::RefCell, future::poll_fn, rc::Rc, task::Context, task::Poll,
3};
4
5use ntex_service::{BindCtx, ReadyNotifier};
6
7use crate::{future::OnDropFn, task::LocalWaker};
8
9#[derive(Debug)]
14pub struct Counter(usize, Rc<CounterInner>);
15
16#[derive(Debug)]
17struct CounterInner {
18 count: Cell<usize>,
19 capacity: Cell<usize>,
20 tasks: RefCell<slab::Slab<LocalWaker>>,
21 changed: OnceCell<ReadyNotifier>,
22}
23
24impl Counter {
25 pub fn new(capacity: usize) -> Self {
27 let mut tasks = slab::Slab::new();
28 let idx = tasks.insert(LocalWaker::new());
29
30 Counter(
31 idx,
32 Rc::new(CounterInner {
33 count: Cell::new(0),
34 capacity: Cell::new(capacity),
35 tasks: RefCell::new(tasks),
36 changed: OnceCell::new(),
37 }),
38 )
39 }
40
41 pub fn get(&self) -> CounterGuard {
46 CounterGuard::new(self.1.clone())
47 }
48
49 pub fn set_capacity(&self, cap: usize) {
51 self.1.capacity.set(cap);
52 self.1.notify();
53 }
54
55 pub fn is_available(&self) -> bool {
57 self.1.count.get() < self.1.capacity.get()
58 }
59
60 pub async fn available(&self) {
65 poll_fn(|cx| {
66 if self.poll_available(cx) {
67 Poll::Ready(())
68 } else {
69 Poll::Pending
70 }
71 })
72 .await;
73 }
74
75 pub async fn unavailable(&self) {
77 poll_fn(|cx| {
78 if self.is_available() {
79 self.1.tasks.borrow()[self.0].register(cx.waker());
80 Poll::Pending
81 } else {
82 Poll::Ready(())
83 }
84 })
85 .await;
86 }
87
88 fn poll_available(&self, cx: &mut Context<'_>) -> bool {
91 if self.1.count.get() < self.1.capacity.get() {
92 true
93 } else {
94 let tasks = self.1.tasks.borrow();
95 tasks[self.0].register(cx.waker());
96 false
97 }
98 }
99
100 pub fn total(&self) -> usize {
102 self.1.count.get()
103 }
104
105 pub fn bind(&self, ctx: &BindCtx<'_>) {
111 ctx.subscribe(self.1.changed.get_or_init(ReadyNotifier::new));
112 }
113}
114
115impl Clone for Counter {
116 fn clone(&self) -> Self {
117 let idx = self.1.tasks.borrow_mut().insert(LocalWaker::new());
118 Self(idx, self.1.clone())
119 }
120}
121
122impl Drop for Counter {
123 fn drop(&mut self) {
124 self.1.tasks.borrow_mut().remove(self.0);
125 }
126}
127
128#[derive(Debug)]
129pub struct CounterGuard(Rc<CounterInner>);
133
134impl CounterGuard {
135 fn new(inner: Rc<CounterInner>) -> Self {
136 let guard = CounterGuard(inner);
137 guard.0.inc();
138 guard
139 }
140}
141
142impl Unpin for CounterGuard {}
143
144impl Drop for CounterGuard {
145 fn drop(&mut self) {
146 self.0.dec();
147 }
148}
149
150impl CounterInner {
151 fn inc(&self) {
152 let num = self.count.get() + 1;
153 self.count.set(num);
154 if num == self.capacity.get() {
155 self.notify();
156 }
157 }
158
159 fn dec(&self) {
160 let num = self.count.get();
161 self.count.set(num - 1);
162 if num == self.capacity.get() {
163 self.notify();
164 }
165 }
166
167 fn notify(&self) {
168 let _wake = OnDropFn::new(|| {
169 let tasks = self.tasks.borrow();
170 for (_, task) in &*tasks {
171 task.wake();
172 }
173 });
174 if let Some(changed) = self.changed.get() {
175 changed.notify();
176 }
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use std::time::Duration;
183
184 use super::*;
185 use crate::time::sleep;
186
187 #[ntex::test]
188 async fn test_unavailable_is_woken() {
189 let counter = Counter::new(2);
190 let done = Rc::new(Cell::new(false));
191
192 let (c, d) = (counter.clone(), done.clone());
193 crate::spawn(async move {
194 c.unavailable().await;
195 d.set(true);
196 });
197 sleep(Duration::from_millis(10)).await;
198 assert!(!done.get());
199
200 let _g1 = counter.get();
201 sleep(Duration::from_millis(10)).await;
202 assert!(!done.get());
203
204 let _g2 = counter.get();
205 sleep(Duration::from_millis(10)).await;
206 assert!(done.get());
207 }
208
209 #[ntex::test]
210 async fn test_available_is_woken() {
211 let counter = Counter::new(1);
212 let guard = counter.get();
213 let done = Rc::new(Cell::new(false));
214
215 let (c, d) = (counter.clone(), done.clone());
216 crate::spawn(async move {
217 c.available().await;
218 d.set(true);
219 });
220 sleep(Duration::from_millis(10)).await;
221 assert!(!done.get());
222
223 drop(guard);
224 sleep(Duration::from_millis(10)).await;
225 assert!(done.get());
226 }
227
228 #[ntex::test]
229 async fn test_set_capacity() {
230 let counter = Counter::new(1);
231 let guard = counter.get();
232 assert_eq!(counter.total(), 1);
233 assert!(!counter.is_available());
234
235 let counter2 = counter.clone();
236 let hnd = crate::spawn(async move { counter2.available().await });
237 sleep(Duration::from_millis(10)).await;
238 assert!(!hnd.is_finished());
239
240 counter.set_capacity(2);
242 assert!(counter.is_available());
243 crate::time::timeout(Duration::from_secs(1), hnd)
244 .await
245 .unwrap()
246 .unwrap();
247
248 drop(guard);
249 assert_eq!(counter.total(), 0);
250 }
251}