1use crate::{
5 Connection, Endpoint, Error, Options, ResponseBody, connection,
6 outgoing::{Observer, OutgoingBody},
7};
8use base_http::{Request, Response, body::ReadableBody};
9use base_pool::{Acquisition, MultiplexBuildPermit, MultiplexPool, MultiplexUse};
10use base_scheduler::{
11 Cancellation, SocketClose, SocketConnect, SocketOpen, SocketSetOption, SocketStreamReading,
12 SocketStreamWriting, TcpNoDelay,
13};
14use event_listener::Event;
15use futures_channel::oneshot;
16use futures_util::stream::{FuturesUnordered, StreamExt};
17use pin_project_lite::pin_project;
18use std::{
19 collections::VecDeque,
20 future::{Future, poll_fn},
21 pin::{Pin, pin},
22 sync::{Arc, Mutex, MutexGuard, PoisonError, atomic::AtomicUsize},
23 task::Poll,
24};
25
26pub(crate) fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
27 mutex.lock().unwrap_or_else(PoisonError::into_inner)
28}
29pub(crate) type Work<'scope, T> = Pin<Box<dyn Future<Output = T> + Send + 'scope>>;
30type Factory<'scope> = Box<
31 dyn Fn(
32 MultiplexBuildPermit<Connection<'scope>>,
33 Cancellation,
34 ) -> Work<'scope, Result<(), Error>>
35 + Send
36 + Sync
37 + 'scope,
38>;
39pub(crate) type Reply = oneshot::Sender<Result<Response<ResponseBody>, Error>>;
40
41pub(crate) struct Operation<'scope> {
42 pub request: Request<OutgoingBody<'scope>>,
43 pub interim: Box<dyn Observer + 'scope>,
44 pub reply: Option<Reply>,
45 pub cancellation: Cancellation,
46 pub lifetime: Cancellation,
47 pub _pending: Option<Pending<'scope>>,
48}
49pub(crate) struct Assigned<'scope> {
50 pub operation: Operation<'scope>,
51 pub active: MultiplexUse<Connection<'scope>>,
52}
53struct State<'scope> {
54 requests: VecDeque<Operation<'scope>>,
55 work: VecDeque<Work<'scope, ()>>,
56 pending: usize,
57 closed: bool,
58 abandoned: bool,
59}
60pub(crate) struct Queue<'scope> {
61 state: Mutex<State<'scope>>,
62 pub changed: Event,
63 pub handles: AtomicUsize,
64 limit: usize,
65}
66impl<'scope> Queue<'scope> {
67 pub fn push(&self, operation: Operation<'scope>) -> Result<(), Error> {
68 let mut state = lock(&self.state);
69 if state.closed {
70 return Err(Error::Closed);
71 }
72 state.requests.push_back(operation);
73 drop(state);
74 self.changed.notify(usize::MAX);
75 Ok(())
76 }
77 pub(crate) fn push_work(&self, work: Work<'scope, ()>) -> Result<(), Error> {
78 let mut state = lock(&self.state);
79 if state.abandoned {
80 drop(state);
81 drop(work);
82 return Err(Error::Closed);
83 }
84 state.work.push_back(work);
85 drop(state);
86 self.changed.notify(usize::MAX);
87 Ok(())
88 }
89 pub fn begin_close(&self) {
90 lock(&self.state).closed = true;
91 self.changed.notify(usize::MAX);
92 }
93 fn abandon(&self) {
94 let discarded = {
95 let mut state = lock(&self.state);
96 state.closed = true;
97 state.abandoned = true;
98 (
99 std::mem::take(&mut state.requests),
100 std::mem::take(&mut state.work),
101 )
102 };
103 self.changed.notify(usize::MAX);
104 drop(discarded);
105 }
106}
107pub(crate) struct Pending<'scope>(Arc<Queue<'scope>>);
108impl<'scope> Pending<'scope> {
109 pub fn new(queue: Arc<Queue<'scope>>) -> Result<Self, Error> {
110 {
111 let mut state = lock(&queue.state);
112 if state.closed {
113 return Err(Error::Closed);
114 }
115 if state.pending >= queue.limit {
116 return Err(Error::Busy);
117 }
118 state.pending += 1;
119 }
120 Ok(Self(queue))
121 }
122}
123impl Drop for Pending<'_> {
124 fn drop(&mut self) {
125 let drained = {
126 let mut state = lock(&self.0.state);
127 state.pending -= 1;
128 state.closed && state.pending == 0
129 };
130 if drained {
133 self.0.changed.notify(usize::MAX);
134 }
135 }
136}
137
138pub(crate) struct Context<'scope> {
139 pool: MultiplexPool<Connection<'scope>>,
140 factory: Factory<'scope>,
141 pub(crate) cancellation: Cancellation,
142}
143impl<'scope> Context<'scope> {
144 pub(crate) async fn acquire(
145 &self,
146 cancellation: &Cancellation,
147 ) -> Result<MultiplexUse<Connection<'scope>>, Error> {
148 loop {
149 let active = match self.pool.acquire_or_reserve(cancellation).await? {
151 Acquisition::Existing(reservation) => {
152 reservation.activate().ok().and_then(|active| {
153 if active.probe() {
154 Some(active)
155 } else {
156 active.complete();
157 None
158 }
159 })
160 }
161 Acquisition::Build(build) => {
162 (self.factory)(build, cancellation.clone()).await?;
163 None
164 }
165 };
166 if let Some(active) = active {
167 return Ok(active);
168 }
169 }
170 }
171 async fn cleanup(&self) -> Result<(), Error> {
172 let mut failure = None;
173 while let Some(retired) = self
174 .pool
175 .next_retirement(&Cancellation::never())
176 .await
177 .expect("an uncancellable retirement wait cannot fail")
178 {
179 retired.stop();
180 if let Err(error) = retired.wait_closed().await {
181 failure.get_or_insert(error);
182 }
183 retired.complete();
184 }
185 failure.map_or(Ok(()), Err)
186 }
187}
188
189pub struct Driver<'scope> {
198 work: Work<'scope, Result<(), Error>>,
199 cancellation: Cancellation,
200 abandon: Box<dyn Fn() + Send + Sync + 'scope>,
201}
202impl Driver<'_> {
203 pub async fn run(mut self, cancellation: &Cancellation) -> Result<(), Error> {
204 let mut cancelled = pin!(cancellation.cancelled());
205 poll_fn(|cx| {
206 if !self.cancellation.is_cancelled() && cancelled.as_mut().poll(cx).is_ready() {
207 self.cancellation.cancel();
208 }
209 self.work.as_mut().poll(cx)
210 })
211 .await
212 }
213}
214impl Drop for Driver<'_> {
215 fn drop(&mut self) {
216 self.cancellation.cancel();
217 (self.abandon)();
218 }
219}
220
221pub(crate) struct ClientParts<'scope> {
222 pub queue: Arc<Queue<'scope>>,
223 pub pool: MultiplexPool<Connection<'scope>>,
224 pub driver: Driver<'scope>,
225 pub context: Arc<Context<'scope>>,
226}
227
228pub(crate) fn make<'scope, S>(
229 scheduler: &'scope S,
230 endpoint: Endpoint,
231 options: Options,
232) -> ClientParts<'scope>
233where
234 S: SocketOpen
235 + SocketConnect
236 + SocketClose
237 + SocketSetOption<TcpNoDelay>
238 + SocketStreamReading
239 + SocketStreamWriting
240 + 'scope,
241 S::Socket: 'static,
242{
243 let pool =
244 MultiplexPool::new(options.pool.clone()).expect("Client::new validated the pool options");
245 let queue = Arc::new(Queue {
246 state: Mutex::new(State {
247 requests: VecDeque::new(),
248 work: VecDeque::new(),
249 pending: 0,
250 closed: false,
251 abandoned: false,
252 }),
253 changed: Event::new(),
254 handles: AtomicUsize::new(1),
255 limit: options.max_requests,
256 });
257 let cancellation = Cancellation::new();
258 let weak_queue = Arc::downgrade(&queue);
259 let connection_lifetime = cancellation.clone();
260 let factory = Box::new(move |build, cancelled: Cancellation| {
261 let options = options.clone();
262 let endpoint = endpoint.clone();
263 let lifetime = connection_lifetime.clone();
264 let queue = weak_queue.upgrade().expect("an acquirer retains the queue");
266 Box::pin(async move {
267 let (ready, established) = oneshot::channel();
268 queue.push_work(Box::pin(connection::run(
269 scheduler,
270 endpoint,
271 options,
272 build,
273 cancelled,
274 lifetime,
275 ready,
276 Arc::downgrade(&queue),
277 )))?;
278 established.await.unwrap_or(Err(Error::Closed))
280 }) as Work<'scope, _>
281 });
282 let context = Arc::new(Context {
283 pool: pool.clone(),
284 factory,
285 cancellation: cancellation.clone(),
286 });
287 let abandoned = queue.clone();
288 let abandoned_pool = pool.clone();
289 let driver = Driver {
290 work: Box::pin(run(queue.clone(), context.clone())),
291 cancellation,
292 abandon: Box::new(move || {
293 abandoned.abandon();
294 abandoned_pool.begin_close();
295 }),
296 };
297 ClientParts {
298 queue,
299 pool,
300 driver,
301 context,
302 }
303}
304
305async fn assign<'scope>(mut operation: Operation<'scope>, context: Arc<Context<'scope>>) {
306 let result = context.acquire(&operation.cancellation).await;
307 match result {
308 Ok(active) => {
309 let connection = active.clone_handle();
310 connection.submit(Assigned { operation, active });
311 }
312 Err(error) => {
313 operation.request.body.close(None);
314 let reply = operation
315 .reply
316 .take()
317 .expect("an unassigned operation retains its reply");
318 let _ = reply.send(Err(error));
319 }
320 }
321}
322
323async fn run<'scope>(
324 queue: Arc<Queue<'scope>>,
325 context: Arc<Context<'scope>>,
326) -> Result<(), Error> {
327 let mut jobs: FuturesUnordered<Work<'scope, ()>> = FuturesUnordered::new();
328 let cleanup_context = context.clone();
329 let mut cleanup = pin!(cleanup_context.cleanup());
330 let mut cancellation = pin!(context.cancellation.cancelled());
331 let mut stopping = false;
332 let mut cleaned = None;
333 loop {
334 event_listener::listener!(queue.changed => listener);
335 let (requests, work) = {
336 let mut state = lock(&queue.state);
337 (
338 std::mem::take(&mut state.requests),
339 std::mem::take(&mut state.work),
340 )
341 };
342 for operation in requests {
343 let context = context.clone();
344 jobs.push(Box::pin(async move {
345 let local = operation.cancellation.clone();
346 let lifetime = operation.lifetime.clone();
347 let driver = context.cancellation.clone();
348 linked(assign(operation, context), &local, &lifetime, &driver).await;
349 }));
350 }
351 jobs.extend(work);
352 let close = {
353 let state = lock(&queue.state);
354 state.closed && state.pending == 0
355 };
356 if stopping || close {
357 context.pool.begin_close();
358 }
359 let mut listener = pin!(listener);
360 let finished = poll_fn(|cx| {
361 if !stopping && cancellation.as_mut().poll(cx).is_ready() {
362 stopping = true;
363 queue.begin_close();
364 context.pool.begin_close();
365 }
366 if cleaned.is_none() {
367 if let Poll::Ready(result) = cleanup.as_mut().poll(cx) {
368 cleaned = Some(result);
369 }
370 }
371 if cleaned.is_some() && jobs.is_empty() && lock(&queue.state).work.is_empty() {
372 return Poll::Ready(cleaned.take());
373 }
374 if let Poll::Ready(Some(())) = jobs.poll_next_unpin(cx) {
375 return Poll::Ready(None);
376 }
377 if listener.as_mut().poll(cx).is_ready() {
378 return Poll::Ready(None);
379 }
380 Poll::Pending
381 })
382 .await;
383 if let Some(result) = finished {
384 return result;
385 }
386 }
387}
388
389pin_project! {
390 struct Checked<'a, F, C> {
391 #[pin]
392 work: F,
393 #[pin]
394 cancelled: C,
395 cancellation: &'a Cancellation,
396 }
397}
398impl<T, F, C> Future for Checked<'_, F, C>
399where
400 F: Future<Output = Result<T, Error>>,
401 C: Future<Output = ()>,
402{
403 type Output = Result<T, Error>;
404 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
405 let this = self.project();
406 if this.cancellation.is_cancelled() {
407 return Poll::Ready(Err(Error::Cancelled));
408 }
409 match this.work.poll(cx) {
410 ready @ Poll::Ready(_) => ready,
411 Poll::Pending => {
412 if this.cancelled.poll(cx).is_ready() {
415 Poll::Ready(Err(Error::Cancelled))
416 } else {
417 Poll::Pending
418 }
419 }
420 }
421 }
422}
423pub(crate) fn checked<T>(
424 work: impl Future<Output = Result<T, Error>>,
425 cancellation: &Cancellation,
426) -> impl Future<Output = Result<T, Error>> {
427 Checked {
430 work,
431 cancelled: cancellation.cancelled(),
432 cancellation,
433 }
434}
435pub(crate) async fn linked<T>(
437 work: impl Future<Output = T>,
438 cancellation: &Cancellation,
439 lifetime: &Cancellation,
440 driver: &Cancellation,
441) -> T {
442 let mut work = pin!(work);
443 let mut a = pin!(lifetime.cancelled());
444 let mut b = pin!(driver.cancelled());
445 poll_fn(|cx| {
446 if !cancellation.is_cancelled()
447 && (a.as_mut().poll(cx).is_ready() || b.as_mut().poll(cx).is_ready())
448 {
449 cancellation.cancel();
450 }
451 work.as_mut().poll(cx)
452 })
453 .await
454}
455pub(crate) struct CancelOnDrop(pub Cancellation, pub bool);
456impl Drop for CancelOnDrop {
457 fn drop(&mut self) {
458 if self.1 {
459 self.0.cancel();
460 }
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use crate::{Endpoint, Options};
468 use base_http::{Method, body::EmptyBody};
469 use base_scheduler_native::NativeScheduler;
470 use std::{
471 sync::atomic::Ordering,
472 task::{Context, Wake, Waker},
473 };
474
475 #[derive(Default)]
476 struct Wakes(AtomicUsize);
477
478 struct Probe<'a>(&'a std::cell::Cell<bool>);
480 impl Future for Probe<'_> {
481 type Output = Result<(), Error>;
482 fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
483 self.0.set(true);
484 Poll::Ready(Ok(()))
485 }
486 }
487 impl Wake for Wakes {
488 fn wake(self: Arc<Self>) {
489 self.0.fetch_add(1, Ordering::Relaxed);
490 }
491 }
492
493 #[test]
494 fn checked_prioritizes_existing_cancellation_and_wakes_blocked_work() {
495 use futures_lite::future::{block_on, poll_once};
496 use std::cell::Cell;
497 let polled = Cell::new(false);
498 block_on(checked(Probe(&polled), &Cancellation::new())).unwrap();
499 assert!(polled.get());
500
501 let cancel = Cancellation::new();
502 cancel.cancel();
503 let polled = Cell::new(false);
504 let error = block_on(checked(Probe(&polled), &cancel)).unwrap_err();
505 assert_eq!(error.to_string(), Error::Cancelled.to_string());
506 assert!(!polled.get());
507
508 let cancel = Cancellation::new();
509 let wakes = Arc::new(Wakes::default());
510 let waker = Waker::from(wakes.clone());
511 let mut cx = Context::from_waker(&waker);
512 let mut work = pin!(checked(
513 std::future::pending::<Result<(), Error>>(),
514 &cancel
515 ));
516 assert!(work.as_mut().poll(&mut cx).is_pending());
517 cancel.cancel();
518 assert!(wakes.0.load(Ordering::Relaxed) > 0);
519 assert_eq!(
520 format!("{:?}", work.as_mut().poll(&mut cx)),
521 "Ready(Err(Cancelled))"
522 );
523
524 let cancel = Cancellation::new();
527 let pending = std::future::poll_fn(|_| {
528 cancel.cancel();
529 Poll::Pending::<Result<(), Error>>
530 });
531 let mut work = pin!(checked(pending, &cancel));
532 assert_eq!(
533 format!("{:?}", block_on(poll_once(work.as_mut()))),
534 "Some(Err(Cancelled))"
535 );
536 }
537
538 #[test]
539 fn pending_completion_wakes_supervisor_only_when_closed_and_drained() {
540 let queue = Arc::new(Queue {
541 state: Mutex::new(State {
542 requests: VecDeque::new(),
543 work: VecDeque::new(),
544 pending: 0,
545 closed: false,
546 abandoned: false,
547 }),
548 changed: Event::new(),
549 handles: AtomicUsize::new(1),
550 limit: 2,
551 });
552 let wakes = Arc::new(Wakes::default());
553 let waker = Waker::from(wakes.clone());
554 let mut cx = Context::from_waker(&waker);
555 let mut changed = pin!(queue.changed.listen());
556 assert!(changed.as_mut().poll(&mut cx).is_pending());
557
558 let first = Pending::new(queue.clone()).unwrap();
559 let second = Pending::new(queue.clone()).unwrap();
560 let error = Pending::new(queue.clone()).err().unwrap();
561 assert_eq!(error.to_string(), Error::Busy.to_string());
562 drop(first);
563 assert_eq!(wakes.0.load(Ordering::Relaxed), 0);
564 let third = Pending::new(queue.clone()).unwrap();
567 queue.begin_close();
568 assert!(changed.as_mut().poll(&mut cx).is_ready());
569 let mut changed = pin!(queue.changed.listen());
570 assert!(changed.as_mut().poll(&mut cx).is_pending());
571 wakes.0.store(0, Ordering::Relaxed);
572 let error = Pending::new(queue.clone()).err().unwrap();
573 assert_eq!(error.to_string(), Error::Closed.to_string());
574 drop(second);
575 assert_eq!(wakes.0.load(Ordering::Relaxed), 0);
576 assert!(changed.as_mut().poll(&mut cx).is_pending());
577 drop(third);
578 assert_eq!(wakes.0.load(Ordering::Relaxed), 1);
579 assert!(changed.as_mut().poll(&mut cx).is_ready());
580 assert_eq!(lock(&queue.state).pending, 0);
581 }
582
583 fn parts(scheduler: &NativeScheduler) -> ClientParts<'_> {
584 let endpoint = Endpoint::new("127.0.0.1:1".parse().unwrap());
585 make(scheduler, endpoint, Options::default())
586 }
587
588 #[test]
589 fn requests_pushed_after_close_are_rejected() {
590 let scheduler = NativeScheduler::new().unwrap();
591 let parts = parts(&scheduler);
592 parts.queue.begin_close();
593 let operation = Operation {
594 request: Request::new(
595 Method::GET,
596 "/",
597 OutgoingBody::new(EmptyBody, Cancellation::never()),
598 ),
599 interim: Box::new(crate::outgoing::tests::ignore),
600 reply: None,
601 cancellation: Cancellation::new(),
602 lifetime: Cancellation::never(),
603 _pending: None,
604 };
605 let error = parts.queue.push(operation).unwrap_err();
606 assert_eq!(error.to_string(), Error::Closed.to_string());
607 assert!(lock(&parts.queue.state).requests.is_empty());
608 }
609
610 #[test]
611 fn construction_after_the_queue_is_abandoned_releases_its_build() {
612 let scheduler = NativeScheduler::new().unwrap();
613 let parts = parts(&scheduler);
614 parts.queue.abandon();
616 let error = futures_lite::future::block_on(parts.context.acquire(&Cancellation::never()))
617 .err()
618 .unwrap();
619 assert_eq!(error.to_string(), Error::Closed.to_string());
620 assert_eq!(parts.pool.stats().total(), 0);
621 }
622}