ntex_mqtt/
inflight.rs

1//! Service that limits number of in-flight async requests.
2use std::{cell::Cell, future::poll_fn, rc::Rc, task::Context, task::Poll};
3
4use ntex_service::{Service, ServiceCtx};
5use ntex_util::{future::join, task::LocalWaker};
6
7/// Trait for types that could be sized
8pub trait SizedRequest {
9    fn size(&self) -> u32;
10
11    fn is_publish(&self) -> bool;
12
13    fn is_chunk(&self) -> bool;
14}
15
16pub struct InFlightServiceImpl<S> {
17    count: Counter,
18    service: S,
19    publish: Cell<bool>,
20}
21
22impl<S> InFlightServiceImpl<S> {
23    pub fn new(max_cap: u16, max_size: usize, service: S) -> Self {
24        InFlightServiceImpl {
25            service,
26            publish: Cell::new(false),
27            count: Counter::new(max_cap, max_size),
28        }
29    }
30}
31
32impl<S, R> Service<R> for InFlightServiceImpl<S>
33where
34    S: Service<R>,
35    R: SizedRequest + 'static,
36{
37    type Response = S::Response;
38    type Error = S::Error;
39
40    #[inline]
41    async fn ready(&self, ctx: ServiceCtx<'_, Self>) -> Result<(), S::Error> {
42        if self.publish.get() || self.count.is_available() {
43            ctx.ready(&self.service).await
44        } else {
45            join(self.count.available(), ctx.ready(&self.service)).await.1
46        }
47    }
48
49    #[inline]
50    async fn call(&self, req: R, ctx: ServiceCtx<'_, Self>) -> Result<S::Response, S::Error> {
51        // process payload chunks
52        if self.publish.get() && !req.is_chunk() {
53            self.publish.set(false);
54        }
55        if req.is_publish() {
56            self.publish.set(true);
57        }
58
59        let size = if self.count.0.max_size > 0 { req.size() } else { 0 };
60        let task_guard = self.count.get(size);
61        let result = ctx.call(&self.service, req).await;
62        drop(task_guard);
63        result
64    }
65
66    ntex_service::forward_poll!(service);
67    ntex_service::forward_shutdown!(service);
68}
69
70struct Counter(Rc<CounterInner>);
71
72struct CounterInner {
73    max_cap: u16,
74    cur_cap: Cell<u16>,
75    max_size: usize,
76    cur_size: Cell<usize>,
77    task: LocalWaker,
78}
79
80impl Counter {
81    fn new(max_cap: u16, max_size: usize) -> Self {
82        Counter(Rc::new(CounterInner {
83            max_cap,
84            max_size,
85            cur_cap: Cell::new(0),
86            cur_size: Cell::new(0),
87            task: LocalWaker::new(),
88        }))
89    }
90
91    fn get(&self, size: u32) -> CounterGuard {
92        CounterGuard::new(size, self.0.clone())
93    }
94
95    fn is_available(&self) -> bool {
96        (self.0.max_cap == 0 || self.0.cur_cap.get() < self.0.max_cap)
97            && (self.0.max_size == 0 || self.0.cur_size.get() <= self.0.max_size)
98    }
99
100    async fn available(&self) {
101        poll_fn(|cx| if self.0.available(cx) { Poll::Ready(()) } else { Poll::Pending }).await
102    }
103}
104
105struct CounterGuard(u32, Rc<CounterInner>);
106
107impl CounterGuard {
108    fn new(size: u32, inner: Rc<CounterInner>) -> Self {
109        inner.inc(size);
110        CounterGuard(size, inner)
111    }
112}
113
114impl Unpin for CounterGuard {}
115
116impl Drop for CounterGuard {
117    fn drop(&mut self) {
118        self.1.dec(self.0);
119    }
120}
121
122impl CounterInner {
123    fn inc(&self, size: u32) {
124        let cur_cap = self.cur_cap.get() + 1;
125        self.cur_cap.set(cur_cap);
126        let cur_size = self.cur_size.get() + size as usize;
127        self.cur_size.set(cur_size);
128
129        if cur_cap == self.max_cap || cur_size >= self.max_size {
130            self.task.wake();
131        }
132    }
133
134    fn dec(&self, size: u32) {
135        let num = self.cur_cap.get();
136        self.cur_cap.set(num - 1);
137
138        let cur_size = self.cur_size.get();
139        let new_size = cur_size - (size as usize);
140        self.cur_size.set(new_size);
141
142        if num == self.max_cap || (cur_size > self.max_size && new_size <= self.max_size) {
143            self.task.wake();
144        }
145    }
146
147    fn available(&self, cx: &Context<'_>) -> bool {
148        self.task.register(cx.waker());
149        (self.max_cap == 0 || self.cur_cap.get() < self.max_cap)
150            && (self.max_size == 0 || self.cur_size.get() <= self.max_size)
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use std::{future::poll_fn, time::Duration};
157
158    use ntex_service::Pipeline;
159    use ntex_util::{future::lazy, task::LocalWaker, time::sleep};
160
161    use super::*;
162
163    struct SleepService(Duration);
164
165    impl Service<()> for SleepService {
166        type Response = ();
167        type Error = ();
168
169        async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<(), ()> {
170            let fut = sleep(self.0);
171            let _ = fut.await;
172            Ok::<_, ()>(())
173        }
174    }
175
176    impl SizedRequest for () {
177        fn size(&self) -> u32 {
178            12
179        }
180
181        fn is_publish(&self) -> bool {
182            false
183        }
184
185        fn is_chunk(&self) -> bool {
186            false
187        }
188    }
189
190    #[ntex::test]
191    async fn test_inflight() {
192        let wait_time = Duration::from_millis(50);
193
194        let srv = Pipeline::new(InFlightServiceImpl::new(1, 0, SleepService(wait_time))).bind();
195        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
196
197        let srv2 = srv.clone();
198        ntex_util::spawn(async move {
199            let _ = srv2.call(()).await;
200        });
201        ntex_util::time::sleep(Duration::from_millis(25)).await;
202        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
203
204        ntex_util::time::sleep(Duration::from_millis(50)).await;
205        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
206        assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
207    }
208
209    #[ntex::test]
210    async fn test_inflight2() {
211        let wait_time = Duration::from_millis(50);
212
213        let srv =
214            Pipeline::new(InFlightServiceImpl::new(0, 10, SleepService(wait_time))).bind();
215        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
216
217        let srv2 = srv.clone();
218        ntex_util::spawn(async move {
219            let _ = srv2.call(()).await;
220        });
221        ntex_util::time::sleep(Duration::from_millis(25)).await;
222        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
223
224        ntex_util::time::sleep(Duration::from_millis(100)).await;
225        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
226    }
227
228    struct Srv2 {
229        dur: Duration,
230        cnt: Cell<bool>,
231        waker: LocalWaker,
232    }
233
234    impl Service<()> for Srv2 {
235        type Response = ();
236        type Error = ();
237
238        async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), ()> {
239            poll_fn(|cx| {
240                if !self.cnt.get() {
241                    Poll::Ready(Ok(()))
242                } else {
243                    self.waker.register(cx.waker());
244                    Poll::Pending
245                }
246            })
247            .await
248        }
249
250        async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<(), ()> {
251            let fut = sleep(self.dur);
252            self.cnt.set(true);
253            self.waker.wake();
254
255            let _ = fut.await;
256            self.cnt.set(false);
257            self.waker.wake();
258            Ok::<_, ()>(())
259        }
260    }
261
262    /// InflightService::poll_ready() must always register waker,
263    /// otherwise it can lose wake up if inner service's poll_ready
264    /// does not wakes dispatcher.
265    #[ntex::test]
266    async fn test_inflight3() {
267        let wait_time = Duration::from_millis(50);
268
269        let srv = Pipeline::new(InFlightServiceImpl::new(
270            1,
271            10,
272            Srv2 { dur: wait_time, cnt: Cell::new(false), waker: LocalWaker::new() },
273        ))
274        .bind();
275        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
276
277        let srv2 = srv.clone();
278        ntex_util::spawn(async move {
279            let _ = srv2.call(()).await;
280        });
281        ntex_util::time::sleep(Duration::from_millis(25)).await;
282        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
283
284        let srv2 = srv.clone();
285        let (tx, rx) = ntex_util::channel::oneshot::channel();
286        ntex_util::spawn(async move {
287            let _ = poll_fn(|cx| srv2.poll_ready(cx)).await;
288            let _ = tx.send(());
289        });
290        assert_eq!(poll_fn(|cx| srv.poll_ready(cx)).await, Ok(()));
291
292        let _ = rx.await;
293    }
294}