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) => reservation.activate().ok(),
152 Acquisition::Build(build) => {
153 (self.factory)(build, cancellation.clone()).await?;
154 None
155 }
156 };
157 if let Some(active) = active {
158 return Ok(active);
159 }
160 }
161 }
162 async fn cleanup(&self) -> Result<(), Error> {
163 let mut failure = None;
164 while let Some(retired) = self
165 .pool
166 .next_retirement(&Cancellation::never())
167 .await
168 .expect("an uncancellable retirement wait cannot fail")
169 {
170 retired.stop();
171 if let Err(error) = retired.wait_closed().await {
172 failure.get_or_insert(error);
173 }
174 retired.complete();
175 }
176 failure.map_or(Ok(()), Err)
177 }
178}
179
180pub struct Driver<'scope> {
189 work: Work<'scope, Result<(), Error>>,
190 cancellation: Cancellation,
191 abandon: Box<dyn Fn() + Send + Sync + 'scope>,
192}
193impl Driver<'_> {
194 pub async fn run(mut self, cancellation: &Cancellation) -> Result<(), Error> {
195 let mut cancelled = pin!(cancellation.cancelled());
196 poll_fn(|cx| {
197 if !self.cancellation.is_cancelled() && cancelled.as_mut().poll(cx).is_ready() {
198 self.cancellation.cancel();
199 }
200 self.work.as_mut().poll(cx)
201 })
202 .await
203 }
204}
205impl Drop for Driver<'_> {
206 fn drop(&mut self) {
207 self.cancellation.cancel();
208 (self.abandon)();
209 }
210}
211
212pub(crate) struct ClientParts<'scope> {
213 pub queue: Arc<Queue<'scope>>,
214 pub pool: MultiplexPool<Connection<'scope>>,
215 pub driver: Driver<'scope>,
216 pub context: Arc<Context<'scope>>,
217}
218
219pub(crate) fn make<'scope, S>(
220 scheduler: &'scope S,
221 endpoint: Endpoint,
222 options: Options,
223) -> ClientParts<'scope>
224where
225 S: SocketOpen
226 + SocketConnect
227 + SocketClose
228 + SocketSetOption<TcpNoDelay>
229 + SocketStreamReading
230 + SocketStreamWriting
231 + 'scope,
232 S::Socket: 'static,
233{
234 let pool =
235 MultiplexPool::new(options.pool.clone()).expect("Client::new validated the pool options");
236 let queue = Arc::new(Queue {
237 state: Mutex::new(State {
238 requests: VecDeque::new(),
239 work: VecDeque::new(),
240 pending: 0,
241 closed: false,
242 abandoned: false,
243 }),
244 changed: Event::new(),
245 handles: AtomicUsize::new(1),
246 limit: options.max_requests,
247 });
248 let cancellation = Cancellation::new();
249 let weak_queue = Arc::downgrade(&queue);
250 let connection_lifetime = cancellation.clone();
251 let factory = Box::new(move |build, cancelled: Cancellation| {
252 let options = options.clone();
253 let endpoint = endpoint.clone();
254 let lifetime = connection_lifetime.clone();
255 let queue = weak_queue.upgrade().expect("an acquirer retains the queue");
257 Box::pin(async move {
258 let (ready, established) = oneshot::channel();
259 queue.push_work(Box::pin(connection::run(
260 scheduler,
261 endpoint,
262 options,
263 build,
264 cancelled,
265 lifetime,
266 ready,
267 Arc::downgrade(&queue),
268 )))?;
269 established.await.unwrap_or(Err(Error::Closed))
271 }) as Work<'scope, _>
272 });
273 let context = Arc::new(Context {
274 pool: pool.clone(),
275 factory,
276 cancellation: cancellation.clone(),
277 });
278 let abandoned = queue.clone();
279 let abandoned_pool = pool.clone();
280 let driver = Driver {
281 work: Box::pin(run(queue.clone(), context.clone())),
282 cancellation,
283 abandon: Box::new(move || {
284 abandoned.abandon();
285 abandoned_pool.begin_close();
286 }),
287 };
288 ClientParts {
289 queue,
290 pool,
291 driver,
292 context,
293 }
294}
295
296async fn assign<'scope>(mut operation: Operation<'scope>, context: Arc<Context<'scope>>) {
297 let result = context.acquire(&operation.cancellation).await;
298 match result {
299 Ok(active) => {
300 let connection = active.clone_handle();
301 connection.submit(Assigned { operation, active });
302 }
303 Err(error) => {
304 operation.request.body.close(None);
305 let reply = operation
306 .reply
307 .take()
308 .expect("an unassigned operation retains its reply");
309 let _ = reply.send(Err(error));
310 }
311 }
312}
313
314async fn run<'scope>(
315 queue: Arc<Queue<'scope>>,
316 context: Arc<Context<'scope>>,
317) -> Result<(), Error> {
318 let mut jobs: FuturesUnordered<Work<'scope, ()>> = FuturesUnordered::new();
319 let cleanup_context = context.clone();
320 let mut cleanup = pin!(cleanup_context.cleanup());
321 let mut cancellation = pin!(context.cancellation.cancelled());
322 let mut stopping = false;
323 let mut cleaned = None;
324 loop {
325 event_listener::listener!(queue.changed => listener);
326 let (requests, work) = {
327 let mut state = lock(&queue.state);
328 (
329 std::mem::take(&mut state.requests),
330 std::mem::take(&mut state.work),
331 )
332 };
333 for operation in requests {
334 let context = context.clone();
335 jobs.push(Box::pin(async move {
336 let local = operation.cancellation.clone();
337 let lifetime = operation.lifetime.clone();
338 let driver = context.cancellation.clone();
339 linked(assign(operation, context), &local, &lifetime, &driver).await;
340 }));
341 }
342 jobs.extend(work);
343 let close = {
344 let state = lock(&queue.state);
345 state.closed && state.pending == 0
346 };
347 if stopping || close {
348 context.pool.begin_close();
349 }
350 let mut listener = pin!(listener);
351 let finished = poll_fn(|cx| {
352 if !stopping && cancellation.as_mut().poll(cx).is_ready() {
353 stopping = true;
354 queue.begin_close();
355 context.pool.begin_close();
356 }
357 if cleaned.is_none() {
358 if let Poll::Ready(result) = cleanup.as_mut().poll(cx) {
359 cleaned = Some(result);
360 }
361 }
362 if cleaned.is_some() && jobs.is_empty() && lock(&queue.state).work.is_empty() {
363 return Poll::Ready(cleaned.take());
364 }
365 if let Poll::Ready(Some(())) = jobs.poll_next_unpin(cx) {
366 return Poll::Ready(None);
367 }
368 if listener.as_mut().poll(cx).is_ready() {
369 return Poll::Ready(None);
370 }
371 Poll::Pending
372 })
373 .await;
374 if let Some(result) = finished {
375 return result;
376 }
377 }
378}
379
380pin_project! {
381 struct Checked<'a, F, C> {
382 #[pin]
383 work: F,
384 #[pin]
385 cancelled: C,
386 cancellation: &'a Cancellation,
387 }
388}
389impl<T, F, C> Future for Checked<'_, F, C>
390where
391 F: Future<Output = Result<T, Error>>,
392 C: Future<Output = ()>,
393{
394 type Output = Result<T, Error>;
395 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
396 let this = self.project();
397 if this.cancellation.is_cancelled() {
398 return Poll::Ready(Err(Error::Cancelled));
399 }
400 match this.work.poll(cx) {
401 ready @ Poll::Ready(_) => ready,
402 Poll::Pending => {
403 if this.cancelled.poll(cx).is_ready() {
406 Poll::Ready(Err(Error::Cancelled))
407 } else {
408 Poll::Pending
409 }
410 }
411 }
412 }
413}
414pub(crate) fn checked<T>(
415 work: impl Future<Output = Result<T, Error>>,
416 cancellation: &Cancellation,
417) -> impl Future<Output = Result<T, Error>> {
418 Checked {
421 work,
422 cancelled: cancellation.cancelled(),
423 cancellation,
424 }
425}
426pub(crate) async fn linked<T>(
428 work: impl Future<Output = T>,
429 cancellation: &Cancellation,
430 lifetime: &Cancellation,
431 driver: &Cancellation,
432) -> T {
433 let mut work = pin!(work);
434 let mut a = pin!(lifetime.cancelled());
435 let mut b = pin!(driver.cancelled());
436 poll_fn(|cx| {
437 if !cancellation.is_cancelled()
438 && (a.as_mut().poll(cx).is_ready() || b.as_mut().poll(cx).is_ready())
439 {
440 cancellation.cancel();
441 }
442 work.as_mut().poll(cx)
443 })
444 .await
445}
446pub(crate) struct CancelOnDrop(pub Cancellation, pub bool);
447impl Drop for CancelOnDrop {
448 fn drop(&mut self) {
449 if self.1 {
450 self.0.cancel();
451 }
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458 use crate::{Endpoint, Options};
459 use base_http::{Method, body::EmptyBody};
460 use base_scheduler_direct::DirectScheduler;
461 use std::{
462 sync::atomic::Ordering,
463 task::{Context, Wake, Waker},
464 };
465
466 #[derive(Default)]
467 struct Wakes(AtomicUsize);
468
469 struct Probe<'a>(&'a std::cell::Cell<bool>);
471 impl Future for Probe<'_> {
472 type Output = Result<(), Error>;
473 fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
474 self.0.set(true);
475 Poll::Ready(Ok(()))
476 }
477 }
478 impl Wake for Wakes {
479 fn wake(self: Arc<Self>) {
480 self.0.fetch_add(1, Ordering::Relaxed);
481 }
482 }
483
484 #[test]
485 fn checked_prioritizes_existing_cancellation_and_wakes_blocked_work() {
486 use futures_lite::future::{block_on, poll_once};
487 use std::cell::Cell;
488 let polled = Cell::new(false);
489 block_on(checked(Probe(&polled), &Cancellation::new())).unwrap();
490 assert!(polled.get());
491
492 let cancel = Cancellation::new();
493 cancel.cancel();
494 let polled = Cell::new(false);
495 let error = block_on(checked(Probe(&polled), &cancel)).unwrap_err();
496 assert_eq!(error.to_string(), Error::Cancelled.to_string());
497 assert!(!polled.get());
498
499 let cancel = Cancellation::new();
500 let wakes = Arc::new(Wakes::default());
501 let waker = Waker::from(wakes.clone());
502 let mut cx = Context::from_waker(&waker);
503 let mut work = pin!(checked(
504 std::future::pending::<Result<(), Error>>(),
505 &cancel
506 ));
507 assert!(work.as_mut().poll(&mut cx).is_pending());
508 cancel.cancel();
509 assert!(wakes.0.load(Ordering::Relaxed) > 0);
510 assert_eq!(
511 format!("{:?}", work.as_mut().poll(&mut cx)),
512 "Ready(Err(Cancelled))"
513 );
514
515 let cancel = Cancellation::new();
518 let pending = std::future::poll_fn(|_| {
519 cancel.cancel();
520 Poll::Pending::<Result<(), Error>>
521 });
522 let mut work = pin!(checked(pending, &cancel));
523 assert_eq!(
524 format!("{:?}", block_on(poll_once(work.as_mut()))),
525 "Some(Err(Cancelled))"
526 );
527 }
528
529 #[test]
530 fn pending_completion_wakes_supervisor_only_when_closed_and_drained() {
531 let queue = Arc::new(Queue {
532 state: Mutex::new(State {
533 requests: VecDeque::new(),
534 work: VecDeque::new(),
535 pending: 0,
536 closed: false,
537 abandoned: false,
538 }),
539 changed: Event::new(),
540 handles: AtomicUsize::new(1),
541 limit: 2,
542 });
543 let wakes = Arc::new(Wakes::default());
544 let waker = Waker::from(wakes.clone());
545 let mut cx = Context::from_waker(&waker);
546 let mut changed = pin!(queue.changed.listen());
547 assert!(changed.as_mut().poll(&mut cx).is_pending());
548
549 let first = Pending::new(queue.clone()).unwrap();
550 let second = Pending::new(queue.clone()).unwrap();
551 let error = Pending::new(queue.clone()).err().unwrap();
552 assert_eq!(error.to_string(), Error::Busy.to_string());
553 drop(first);
554 assert_eq!(wakes.0.load(Ordering::Relaxed), 0);
555 let third = Pending::new(queue.clone()).unwrap();
558 queue.begin_close();
559 assert!(changed.as_mut().poll(&mut cx).is_ready());
560 let mut changed = pin!(queue.changed.listen());
561 assert!(changed.as_mut().poll(&mut cx).is_pending());
562 wakes.0.store(0, Ordering::Relaxed);
563 let error = Pending::new(queue.clone()).err().unwrap();
564 assert_eq!(error.to_string(), Error::Closed.to_string());
565 drop(second);
566 assert_eq!(wakes.0.load(Ordering::Relaxed), 0);
567 assert!(changed.as_mut().poll(&mut cx).is_pending());
568 drop(third);
569 assert_eq!(wakes.0.load(Ordering::Relaxed), 1);
570 assert!(changed.as_mut().poll(&mut cx).is_ready());
571 assert_eq!(lock(&queue.state).pending, 0);
572 }
573
574 fn parts(scheduler: &DirectScheduler) -> ClientParts<'_> {
575 let endpoint = Endpoint::new("127.0.0.1:1".parse().unwrap());
576 make(scheduler, endpoint, Options::default())
577 }
578
579 #[test]
580 fn requests_pushed_after_close_are_rejected() {
581 let scheduler = DirectScheduler::new(Default::default()).unwrap();
582 let parts = parts(&scheduler);
583 parts.queue.begin_close();
584 let operation = Operation {
585 request: Request::new(
586 Method::GET,
587 "/",
588 OutgoingBody::new(EmptyBody, Cancellation::never()),
589 ),
590 interim: Box::new(crate::outgoing::tests::ignore),
591 reply: None,
592 cancellation: Cancellation::new(),
593 lifetime: Cancellation::never(),
594 _pending: None,
595 };
596 let error = parts.queue.push(operation).unwrap_err();
597 assert_eq!(error.to_string(), Error::Closed.to_string());
598 assert!(lock(&parts.queue.state).requests.is_empty());
599 }
600
601 #[test]
602 fn construction_after_the_queue_is_abandoned_releases_its_build() {
603 let scheduler = DirectScheduler::new(Default::default()).unwrap();
604 let parts = parts(&scheduler);
605 parts.queue.abandon();
607 let error = futures_lite::future::block_on(parts.context.acquire(&Cancellation::never()))
608 .err()
609 .unwrap();
610 assert_eq!(error.to_string(), Error::Closed.to_string());
611 assert_eq!(parts.pool.stats().total(), 0);
612 }
613}