1use 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
7pub 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 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 #[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}