1use std::collections::{HashMap, VecDeque};
2use std::future::Future;
3use std::ops::{Deref, DerefMut};
4use std::sync::{Arc, Mutex, MutexGuard};
5use std::time::Instant;
6
7use async_trait::async_trait;
8use tokio::sync::Notify;
9use tokio::time::Instant as TokioInstant;
10use tokio_util::sync::CancellationToken;
11
12use crate::{
13 AcquireError, ClientPolicy, OptionsError, PoolDirective, PoolOptions,
14};
15
16#[path = "reservation.rs"]
17mod reservation;
18use reservation::Pending;
19
20#[async_trait]
25pub trait ClientManager: Send + Sync + 'static {
26 type Client: Send + 'static;
27 type Error: std::error::Error + Send + Sync + 'static;
28
29 async fn create(&self) -> Result<Self::Client, Self::Error>;
31
32 async fn recycle(
34 &self,
35 client: &mut Self::Client,
36 ) -> Result<(), Self::Error>;
37}
38
39#[derive(Clone, Debug, Eq, PartialEq)]
41pub struct PoolStats {
42 pub live: usize,
43 pub idle: usize,
44 pub leased: usize,
45 pub creating: usize,
46 pub waiting: usize,
47 pub in_flight: usize,
49 pub state: PoolState,
50}
51
52#[derive(Clone, Debug, Eq, PartialEq)]
54pub enum PoolState {
55 Open,
56 Paused { reason: Arc<str> },
57 Draining,
58 Closed,
59}
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub enum ActiveRequestPolicy {
64 Preserve,
66 CancelOnDrain(Option<std::time::Duration>),
68}
69
70struct ActiveCancellation {
71 policy: ActiveRequestPolicy,
72 token: CancellationToken,
73}
74
75struct Entry<C> {
76 client: C,
77 created_at: Instant,
78 idle_since: Instant,
79}
80
81struct State<C> {
82 idle: VecDeque<Entry<C>>,
83 live: usize,
84 creating: usize,
85 waiting: usize,
86 in_flight: usize,
87 status: PoolState,
88 next_active_id: u64,
89 drain_started: Option<Instant>,
90 drain_cancellations: HashMap<u64, ActiveCancellation>,
91}
92
93struct Inner<M: ClientManager> {
94 manager: M,
95 options: PoolOptions,
96 state: Mutex<State<M::Client>>,
97 changed: Notify,
98 admission_closed: CancellationToken,
99 active_cancellation: CancellationToken,
100}
101
102pub struct ClientPool<M: ClientManager> {
104 inner: Arc<Inner<M>>,
105}
106
107impl<M: ClientManager> Clone for ClientPool<M> {
108 fn clone(&self) -> Self {
109 Self { inner: Arc::clone(&self.inner) }
110 }
111}
112
113impl<M: ClientManager> ClientPool<M> {
114 pub fn new(
116 manager: M,
117 options: PoolOptions,
118 ) -> Result<Self, OptionsError> {
119 options.validate()?;
120 Ok(Self {
121 inner: Arc::new(Inner {
122 manager,
123 options,
124 state: Mutex::new(State {
125 idle: VecDeque::new(),
126 live: 0,
127 creating: 0,
128 waiting: 0,
129 in_flight: 0,
130 status: PoolState::Open,
131 next_active_id: 0,
132 drain_started: None,
133 drain_cancellations: HashMap::new(),
134 }),
135 changed: Notify::new(),
136 admission_closed: CancellationToken::new(),
137 active_cancellation: CancellationToken::new(),
138 }),
139 })
140 }
141
142 pub async fn acquire(
144 &self,
145 active_policy: ActiveRequestPolicy,
146 ) -> Result<ClientLease<M>, AcquireError<M::Error>> {
147 self.acquire_inner(active_policy, None).await
148 }
149
150 pub async fn acquire_cancellable(
152 &self,
153 active_policy: ActiveRequestPolicy,
154 cancellation: &CancellationToken,
155 ) -> Result<ClientLease<M>, AcquireError<M::Error>> {
156 self.acquire_inner(active_policy, Some(cancellation)).await
157 }
158
159 async fn acquire_inner(
160 &self,
161 active_policy: ActiveRequestPolicy,
162 cancellation: Option<&CancellationToken>,
163 ) -> Result<ClientLease<M>, AcquireError<M::Error>> {
164 let deadline = self
165 .inner
166 .options
167 .acquire_timeout
168 .map(|duration| TokioInstant::now() + duration);
169 let mut waiter = Waiter::new(self);
170
171 loop {
172 if cancellation.is_some_and(CancellationToken::is_cancelled) {
173 return Err(AcquireError::Cancelled);
174 }
175 let notified = self.inner.changed.notified();
176 let action = {
177 let mut state = self.state();
178 self.prune_expired(&mut state);
179 match &state.status {
180 PoolState::Draining | PoolState::Closed => {
181 return Err(AcquireError::Closed);
182 }
183 PoolState::Paused { .. } => {
184 waiter.enter(&mut state)?;
185 Action::Wait
186 }
187 PoolState::Open => {
188 if state.in_flight >= self.inner.options.max_concurrency
189 {
190 waiter.enter(&mut state)?;
191 Action::Wait
192 } else if let Some(entry) = state.idle.pop_front() {
193 state.in_flight += 1;
194 waiter.leave(&mut state);
195 Action::Reuse(entry)
196 } else if state.live < self.inner.options.max_live {
197 state.live += 1;
198 state.creating += 1;
199 state.in_flight += 1;
200 waiter.leave(&mut state);
201 Action::Create
202 } else {
203 waiter.enter(&mut state)?;
204 Action::Wait
205 }
206 }
207 }
208 };
209
210 match action {
211 Action::Reuse(entry) => {
212 let mut pending = Pending::recycle(self, entry);
213 match self
214 .operation(
215 self.inner.manager.recycle(pending.client()),
216 cancellation,
217 )
218 .await
219 {
220 Ok(Ok(())) => {}
221 Ok(Err(error)) => {
222 return Err(AcquireError::Manager(error));
223 }
224 Err(interrupted) => {
225 return Err(interrupted.acquire_error());
226 }
227 }
228 return Ok(pending.lease(active_policy));
229 }
230 Action::Create => {
231 let pending = Pending::create(self, true);
232 match self
233 .operation(self.inner.manager.create(), cancellation)
234 .await
235 {
236 Ok(Ok(client)) => {
237 return Ok(pending.created(client, active_policy));
238 }
239 Ok(Err(error)) => {
240 return Err(AcquireError::Manager(error));
241 }
242 Err(interrupted) => {
243 return Err(interrupted.acquire_error());
244 }
245 }
246 }
247 Action::Wait => {
248 self.wait_for_change(notified, deadline, cancellation)
249 .await?
250 }
251 }
252 }
253 }
254
255 #[cfg(feature = "fleet")]
256 pub(crate) async fn create(
257 &self,
258 active_policy: ActiveRequestPolicy,
259 ) -> Result<ClientLease<M>, AcquireError<M::Error>> {
260 self.create_inner(active_policy, None).await
261 }
262
263 #[cfg(feature = "fleet")]
264 pub(crate) async fn create_cancellable(
265 &self,
266 active_policy: ActiveRequestPolicy,
267 cancellation: &CancellationToken,
268 ) -> Result<ClientLease<M>, AcquireError<M::Error>> {
269 self.create_inner(active_policy, Some(cancellation)).await
270 }
271
272 #[cfg(feature = "fleet")]
273 async fn create_inner(
274 &self,
275 active_policy: ActiveRequestPolicy,
276 cancellation: Option<&CancellationToken>,
277 ) -> Result<ClientLease<M>, AcquireError<M::Error>> {
278 {
279 let mut state = self.state();
280 self.prune_expired(&mut state);
281 if !matches!(&state.status, PoolState::Open) {
282 return Err(AcquireError::Closed);
283 }
284 if state.live >= self.inner.options.max_live {
285 return Err(AcquireError::AtCapacity);
286 }
287 if state.in_flight >= self.inner.options.max_concurrency {
288 return Err(AcquireError::AtCapacity);
289 }
290 state.live += 1;
291 state.creating += 1;
292 state.in_flight += 1;
293 }
294 let pending = Pending::create(self, true);
295 match self.operation(self.inner.manager.create(), cancellation).await {
296 Ok(Ok(client)) => Ok(pending.created(client, active_policy)),
297 Ok(Err(error)) => Err(AcquireError::Manager(error)),
298 Err(interrupted) => Err(interrupted.acquire_error()),
299 }
300 }
301
302 #[cfg(feature = "fleet")]
303 pub(crate) async fn acquire_idle_where(
304 &self,
305 predicate: impl Fn(&M::Client) -> bool,
306 active_policy: ActiveRequestPolicy,
307 ) -> Result<Option<ClientLease<M>>, AcquireError<M::Error>> {
308 self.acquire_idle_where_inner(predicate, active_policy, None).await
309 }
310
311 #[cfg(feature = "fleet")]
312 pub(crate) async fn acquire_idle_where_cancellable(
313 &self,
314 predicate: impl Fn(&M::Client) -> bool,
315 active_policy: ActiveRequestPolicy,
316 cancellation: &CancellationToken,
317 ) -> Result<Option<ClientLease<M>>, AcquireError<M::Error>> {
318 self.acquire_idle_where_inner(
319 predicate,
320 active_policy,
321 Some(cancellation),
322 )
323 .await
324 }
325
326 #[cfg(feature = "fleet")]
327 async fn acquire_idle_where_inner(
328 &self,
329 predicate: impl Fn(&M::Client) -> bool,
330 active_policy: ActiveRequestPolicy,
331 cancellation: Option<&CancellationToken>,
332 ) -> Result<Option<ClientLease<M>>, AcquireError<M::Error>> {
333 let entry = {
334 let mut state = self.state();
335 self.prune_expired(&mut state);
336 if !matches!(&state.status, PoolState::Open) {
337 return Err(AcquireError::Closed);
338 }
339 if state.in_flight >= self.inner.options.max_concurrency {
340 return Err(AcquireError::AtCapacity);
341 }
342 let Some(index) =
343 state.idle.iter().position(|entry| predicate(&entry.client))
344 else {
345 return Ok(None);
346 };
347 state.in_flight += 1;
348 state.idle.remove(index).expect("idle index came from this queue")
349 };
350 let mut pending = Pending::recycle(self, entry);
351 match self
352 .operation(
353 self.inner.manager.recycle(pending.client()),
354 cancellation,
355 )
356 .await
357 {
358 Ok(Ok(())) => {}
359 Ok(Err(error)) => {
360 return Err(AcquireError::Manager(error));
361 }
362 Err(interrupted) => {
363 return Err(interrupted.acquire_error());
364 }
365 }
366 Ok(Some(pending.lease(active_policy)))
367 }
368
369 #[cfg(feature = "fleet")]
370 pub(crate) fn discard_idle_where(
371 &self,
372 predicate: impl Fn(&M::Client) -> bool,
373 ) -> bool {
374 let mut state = self.state();
375 let Some(index) =
376 state.idle.iter().position(|entry| predicate(&entry.client))
377 else {
378 return false;
379 };
380 let retired =
381 state.idle.remove(index).expect("idle index came from this queue");
382 state.live = state.live.saturating_sub(1);
383 drop(state);
384 drop(retired);
385 self.inner.changed.notify_waiters();
386 true
387 }
388
389 pub async fn run(
391 &self,
392 cancellation: CancellationToken,
393 ) -> Result<(), M::Error> {
394 loop {
395 if cancellation.is_cancelled() {
396 self.drain().await;
397 return Ok(());
398 }
399
400 self.prune_now();
401 if matches!(
402 self.stats().state,
403 PoolState::Draining | PoolState::Closed
404 ) {
405 self.drain().await;
406 return Ok(());
407 }
408 if self.reserve_warm_create() {
409 let pending = Pending::create(self, false);
410 match self
411 .operation(self.inner.manager.create(), Some(&cancellation))
412 .await
413 {
414 Ok(Ok(client)) => pending.warm(client),
415 Ok(Err(error)) => {
416 return Err(error);
417 }
418 Err(_) => {
419 drop(pending);
422 self.drain().await;
423 return Ok(());
424 }
425 }
426 continue;
427 }
428
429 let notified = self.inner.changed.notified();
430 let deadline = self.next_retirement_deadline();
431 match deadline {
432 Some(deadline) => {
433 tokio::select! {
434 _ = cancellation.cancelled() => {},
435 _ = notified => {},
436 _ = tokio::time::sleep_until(TokioInstant::from_std(deadline)) => {}
437 }
438 }
439 None => {
440 tokio::select! {
441 _ = cancellation.cancelled() => {},
442 _ = notified => {}
443 }
444 }
445 }
446 }
447 }
448
449 pub fn close(&self) {
451 let mut state = self.state();
452 state.status = PoolState::Closed;
453 let idle = state.idle.len();
454 let retired: Vec<_> = state.idle.drain(..).collect();
455 state.live = state.live.saturating_sub(idle);
456 drop(state);
457 drop(retired);
458 self.inner.admission_closed.cancel();
459 self.inner.changed.notify_waiters();
460 }
461
462 pub async fn drain(&self) {
464 let immediate = {
465 let mut state = self.state();
466 if !matches!(&state.status, PoolState::Closed) {
467 state.status = PoolState::Draining;
468 }
469 let drain_started =
470 *state.drain_started.get_or_insert_with(Instant::now);
471 let idle = state.idle.len();
472 let retired: Vec<_> = state.idle.drain(..).collect();
473 state.live = state.live.saturating_sub(idle);
474 let immediate = take_due_cancellations(&mut state, drain_started);
475 drop(state);
476 drop(retired);
477 immediate
478 };
479 for cancellation in immediate {
480 cancellation.cancel();
481 }
482 self.inner.admission_closed.cancel();
483 self.inner.changed.notify_waiters();
484 loop {
485 let notified = self.inner.changed.notified();
486 let (due, deadline) = {
487 let mut state = self.state();
488 if state.live == 0 {
489 state.status = PoolState::Closed;
490 drop(state);
491 self.inner.changed.notify_waiters();
492 return;
493 }
494 let now = Instant::now();
495 let due = take_due_cancellations(&mut state, now);
496 let deadline = next_active_cancellation_deadline(&state);
497 (due, deadline)
498 };
499 for cancellation in due {
500 cancellation.cancel();
501 }
502 match deadline {
503 Some(deadline) => {
504 tokio::select! {
505 _ = notified => {},
506 _ = tokio::time::sleep_until(TokioInstant::from_std(deadline)) => {}
507 }
508 }
509 None => notified.await,
510 }
511 }
512 }
513
514 pub fn pause(
516 &self,
517 reason: impl Into<Arc<str>>,
518 ) {
519 let mut state = self.state();
520 if matches!(&state.status, PoolState::Open | PoolState::Paused { .. }) {
521 state.status = PoolState::Paused { reason: reason.into() };
522 }
523 drop(state);
524 self.inner.changed.notify_waiters();
525 }
526
527 pub fn resume(&self) -> bool {
529 let mut state = self.state();
530 if !matches!(&state.status, PoolState::Paused { .. }) {
531 return false;
532 }
533 state.status = PoolState::Open;
534 drop(state);
535 self.inner.changed.notify_waiters();
536 true
537 }
538
539 pub fn apply(
541 &self,
542 directive: PoolDirective,
543 ) {
544 match directive {
545 PoolDirective::Continue => {}
546 PoolDirective::Pause { reason } => self.pause(reason),
547 }
548 }
549
550 pub fn cancel_active(&self) -> bool {
556 if !matches!(
557 &self.state().status,
558 PoolState::Draining | PoolState::Closed
559 ) {
560 return false;
561 }
562 self.inner.active_cancellation.cancel();
563 true
564 }
565
566 fn register_active(
567 &self,
568 policy: ActiveRequestPolicy,
569 ) -> (CancellationToken, Option<u64>) {
570 let token = self.inner.active_cancellation.child_token();
571 if policy == ActiveRequestPolicy::Preserve {
572 return (token, None);
573 }
574
575 let mut state = self.state();
576 let id = state.next_active_id;
577 state.next_active_id = state.next_active_id.wrapping_add(1);
578 let cancel_now = match (policy, state.drain_started) {
579 (ActiveRequestPolicy::CancelOnDrain(None), Some(_)) => true,
580 (
581 ActiveRequestPolicy::CancelOnDrain(Some(duration)),
582 Some(started),
583 ) => started
584 .checked_add(duration)
585 .is_some_and(|deadline| deadline <= Instant::now()),
586 _ => false,
587 };
588 if cancel_now {
589 token.cancel();
590 (token, None)
591 } else {
592 state.drain_cancellations.insert(
593 id,
594 ActiveCancellation { policy, token: token.clone() },
595 );
596 drop(state);
597 self.inner.changed.notify_waiters();
598 (token, Some(id))
599 }
600 }
601
602 fn unregister_active(
603 &self,
604 registration: Option<u64>,
605 ) {
606 let Some(id) = registration else {
607 return;
608 };
609 self.state().drain_cancellations.remove(&id);
610 self.inner.changed.notify_waiters();
611 }
612
613 #[cfg(feature = "fleet")]
614 pub(crate) async fn wait_until_open(
615 &self,
616 deadline: Option<TokioInstant>,
617 cancellation: Option<&CancellationToken>,
618 ) -> Result<(), AcquireError<M::Error>> {
619 let mut waiter = Waiter::new(self);
620 loop {
621 if cancellation.is_some_and(CancellationToken::is_cancelled) {
622 return Err(AcquireError::Cancelled);
623 }
624 let notified = self.inner.changed.notified();
625 {
626 let mut state = self.state();
627 match &state.status {
628 PoolState::Open => {
629 waiter.leave(&mut state);
630 return Ok(());
631 }
632 PoolState::Paused { .. } => waiter.enter(&mut state)?,
633 PoolState::Draining | PoolState::Closed => {
634 return Err(AcquireError::Closed);
635 }
636 }
637 }
638 self.wait_for_change(notified, deadline, cancellation).await?;
639 }
640 }
641
642 #[cfg(feature = "fleet")]
643 pub(crate) async fn wait_until_open_for_maintenance(
644 &self,
645 cancellation: &CancellationToken,
646 ) -> bool {
647 loop {
648 let notified = self.inner.changed.notified();
649 match &self.state().status {
650 PoolState::Open => return true,
651 PoolState::Draining | PoolState::Closed => return false,
652 PoolState::Paused { .. } => {}
653 }
654 tokio::select! {
655 _ = notified => {},
656 _ = cancellation.cancelled() => return false
657 }
658 }
659 }
660
661 pub fn stats(&self) -> PoolStats {
662 let state = self.state();
663 PoolStats {
664 live: state.live,
665 idle: state.idle.len(),
666 leased: state
667 .live
668 .saturating_sub(state.idle.len() + state.creating),
669 creating: state.creating,
670 waiting: state.waiting,
671 in_flight: state.in_flight,
672 state: state.status.clone(),
673 }
674 }
675
676 fn state(&self) -> MutexGuard<'_, State<M::Client>> {
677 self.inner.state.lock().unwrap_or_else(|poison| poison.into_inner())
678 }
679
680 fn finish_create(
681 &self,
682 succeeded: bool,
683 admitted: bool,
684 ) {
685 let mut state = self.state();
686 state.creating = state.creating.saturating_sub(1);
687 if !succeeded {
688 state.live = state.live.saturating_sub(1);
689 if admitted {
690 state.in_flight = state.in_flight.saturating_sub(1);
691 }
692 }
693 drop(state);
694 self.inner.changed.notify_waiters();
695 }
696
697 fn finish_warm_create(
698 &self,
699 client: M::Client,
700 ) {
701 let now = Instant::now();
702 let mut state = self.state();
703 state.creating = state.creating.saturating_sub(1);
704 if matches!(&state.status, PoolState::Draining | PoolState::Closed)
705 || state.idle.len() >= self.inner.options.max_idle
706 {
707 state.live = state.live.saturating_sub(1);
708 } else {
709 state.idle.push_back(Entry {
710 client,
711 created_at: now,
712 idle_since: now,
713 });
714 }
715 drop(state);
716 self.inner.changed.notify_waiters();
717 }
718
719 fn reserve_warm_create(&self) -> bool {
720 let mut state = self.state();
721 if !matches!(&state.status, PoolState::Open)
722 || state.idle.len() + state.creating >= self.inner.options.min_idle
723 || state.live >= self.inner.options.max_live
724 {
725 return false;
726 }
727 state.live += 1;
728 state.creating += 1;
729 true
730 }
731
732 fn retire_one(&self) {
733 let mut state = self.state();
734 state.live = state.live.saturating_sub(1);
735 state.in_flight = state.in_flight.saturating_sub(1);
736 drop(state);
737 self.inner.changed.notify_waiters();
738 }
739
740 fn return_entry(
741 &self,
742 mut entry: Entry<M::Client>,
743 ) {
744 let now = Instant::now();
745 let mut state = self.state();
746 state.in_flight = state.in_flight.saturating_sub(1);
747 let reusable =
748 matches!(&state.status, PoolState::Open | PoolState::Paused { .. })
749 && state.idle.len() < self.inner.options.max_idle
750 && !expired(&entry, now, &self.inner.options);
751 if reusable {
752 entry.idle_since = now;
753 state.idle.push_back(entry);
754 } else {
755 state.live = state.live.saturating_sub(1);
756 }
757 drop(state);
758 self.inner.changed.notify_waiters();
759 }
760
761 fn prune_now(&self) {
762 let mut state = self.state();
763 self.prune_expired(&mut state);
764 }
765
766 fn prune_expired(
767 &self,
768 state: &mut State<M::Client>,
769 ) {
770 let now = Instant::now();
771 let before = state.idle.len();
772 state.idle.retain(|entry| !expired(entry, now, &self.inner.options));
773 state.live = state.live.saturating_sub(before - state.idle.len());
774 }
775
776 fn next_retirement_deadline(&self) -> Option<Instant> {
777 let state = self.state();
778 state
779 .idle
780 .iter()
781 .filter_map(|entry| retirement_deadline(entry, &self.inner.options))
782 .min()
783 }
784
785 async fn wait_for_change<F>(
786 &self,
787 notified: F,
788 deadline: Option<TokioInstant>,
789 cancellation: Option<&CancellationToken>,
790 ) -> Result<(), AcquireError<M::Error>>
791 where
792 F: Future<Output = ()>,
793 {
794 match (deadline, cancellation) {
795 (Some(deadline), Some(cancellation)) => {
796 tokio::select! {
797 _ = notified => Ok(()),
798 _ = cancellation.cancelled() => Err(AcquireError::Cancelled),
799 _ = tokio::time::sleep_until(deadline) => Err(AcquireError::Timeout(
800 self.inner.options.acquire_timeout.expect("deadline has duration")
801 ))
802 }
803 }
804 (Some(deadline), None) => {
805 tokio::select! {
806 _ = notified => Ok(()),
807 _ = tokio::time::sleep_until(deadline) => Err(AcquireError::Timeout(
808 self.inner.options.acquire_timeout.expect("deadline has duration")
809 ))
810 }
811 }
812 (None, Some(cancellation)) => {
813 tokio::select! {
814 _ = notified => Ok(()),
815 _ = cancellation.cancelled() => Err(AcquireError::Cancelled)
816 }
817 }
818 (None, None) => {
819 notified.await;
820 Ok(())
821 }
822 }
823 }
824
825 async fn operation<F, T>(
826 &self,
827 operation: F,
828 cancellation: Option<&CancellationToken>,
829 ) -> Result<T, OperationInterrupted>
830 where
831 F: Future<Output = T>,
832 {
833 match cancellation {
834 Some(cancellation) => tokio::select! {
835 result = operation => Ok(result),
836 _ = cancellation.cancelled() => Err(OperationInterrupted::Caller),
837 _ = self.inner.admission_closed.cancelled() => {
838 Err(OperationInterrupted::AdmissionClosed)
839 }
840 },
841 None => tokio::select! {
842 result = operation => Ok(result),
843 _ = self.inner.admission_closed.cancelled() => {
844 Err(OperationInterrupted::AdmissionClosed)
845 }
846 },
847 }
848 }
849}
850
851enum OperationInterrupted {
852 Caller,
853 AdmissionClosed,
854}
855
856impl OperationInterrupted {
857 fn acquire_error<E>(self) -> AcquireError<E> {
858 match self {
859 Self::Caller => AcquireError::Cancelled,
860 Self::AdmissionClosed => AcquireError::Closed,
861 }
862 }
863}
864
865enum Action<C> {
866 Reuse(Entry<C>),
867 Create,
868 Wait,
869}
870
871struct Waiter<'a, M: ClientManager> {
872 pool: &'a ClientPool<M>,
873 entered: bool,
874}
875
876impl<'a, M: ClientManager> Waiter<'a, M> {
877 fn new(pool: &'a ClientPool<M>) -> Self {
878 Self { pool, entered: false }
879 }
880
881 fn enter(
882 &mut self,
883 state: &mut State<M::Client>,
884 ) -> Result<(), AcquireError<M::Error>> {
885 if self.entered {
886 return Ok(());
887 }
888 if state.waiting >= self.pool.inner.options.max_waiting {
889 return Err(AcquireError::QueueFull {
890 max_waiting: self.pool.inner.options.max_waiting,
891 });
892 }
893 state.waiting += 1;
894 self.entered = true;
895 Ok(())
896 }
897
898 fn leave(
899 &mut self,
900 state: &mut State<M::Client>,
901 ) {
902 if self.entered {
903 state.waiting = state.waiting.saturating_sub(1);
904 self.entered = false;
905 }
906 }
907}
908
909impl<M: ClientManager> Drop for Waiter<'_, M> {
910 fn drop(&mut self) {
911 if !self.entered {
912 return;
913 }
914 let mut state = self.pool.state();
915 state.waiting = state.waiting.saturating_sub(1);
916 drop(state);
917 self.pool.inner.changed.notify_waiters();
918 }
919}
920
921pub struct ClientLease<M: ClientManager> {
923 pool: ClientPool<M>,
924 entry: Option<Entry<M::Client>>,
925 active_cancellation: CancellationToken,
926 active_registration: Option<u64>,
927}
928
929impl<M: ClientManager> ClientLease<M> {
930 fn new(
931 pool: ClientPool<M>,
932 entry: Entry<M::Client>,
933 active_policy: ActiveRequestPolicy,
934 ) -> Self {
935 let (active_cancellation, active_registration) =
936 pool.register_active(active_policy);
937 Self {
938 pool,
939 entry: Some(entry),
940 active_cancellation,
941 active_registration,
942 }
943 }
944
945 pub fn discard(mut self) {
947 self.entry.take();
948 self.pool.unregister_active(self.active_registration.take());
949 self.pool.retire_one();
950 }
951
952 pub fn observe<O, P: ClientPolicy<O>>(
954 &self,
955 policy: &P,
956 outcome: &O,
957 ) {
958 self.pool.apply(policy.classify(outcome));
959 }
960
961 pub fn cancellation_token(&self) -> CancellationToken {
966 self.active_cancellation.clone()
967 }
968}
969
970impl<M: ClientManager> Deref for ClientLease<M> {
971 type Target = M::Client;
972
973 fn deref(&self) -> &Self::Target {
974 &self.entry.as_ref().expect("client lease is live").client
975 }
976}
977
978impl<M: ClientManager> DerefMut for ClientLease<M> {
979 fn deref_mut(&mut self) -> &mut Self::Target {
980 &mut self.entry.as_mut().expect("client lease is live").client
981 }
982}
983
984impl<M: ClientManager> Drop for ClientLease<M> {
985 fn drop(&mut self) {
986 self.pool.unregister_active(self.active_registration.take());
987 if let Some(entry) = self.entry.take() {
988 self.pool.return_entry(entry);
989 }
990 }
991}
992
993fn take_due_cancellations<C>(
994 state: &mut State<C>,
995 now: Instant,
996) -> Vec<CancellationToken> {
997 let Some(started) = state.drain_started else {
998 return Vec::new();
999 };
1000 let due: Vec<_> = state
1001 .drain_cancellations
1002 .iter()
1003 .filter_map(|(id, active)| {
1004 let due = match active.policy {
1005 ActiveRequestPolicy::Preserve => false,
1006 ActiveRequestPolicy::CancelOnDrain(None) => true,
1007 ActiveRequestPolicy::CancelOnDrain(Some(grace)) => started
1008 .checked_add(grace)
1009 .is_some_and(|deadline| deadline <= now),
1010 };
1011 due.then_some(*id)
1012 })
1013 .collect();
1014 due.into_iter()
1015 .filter_map(|id| {
1016 state.drain_cancellations.remove(&id).map(|active| active.token)
1017 })
1018 .collect()
1019}
1020
1021fn next_active_cancellation_deadline<C>(state: &State<C>) -> Option<Instant> {
1022 let started = state.drain_started?;
1023 state
1024 .drain_cancellations
1025 .values()
1026 .filter_map(|active| match active.policy {
1027 ActiveRequestPolicy::CancelOnDrain(Some(grace)) => {
1028 started.checked_add(grace)
1029 }
1030 ActiveRequestPolicy::Preserve
1031 | ActiveRequestPolicy::CancelOnDrain(None) => None,
1032 })
1033 .min()
1034}
1035
1036fn expired<C>(
1037 entry: &Entry<C>,
1038 now: Instant,
1039 options: &PoolOptions,
1040) -> bool {
1041 retirement_deadline(entry, options).is_some_and(|deadline| deadline <= now)
1042}
1043
1044fn retirement_deadline<C>(
1045 entry: &Entry<C>,
1046 options: &PoolOptions,
1047) -> Option<Instant> {
1048 let idle = options
1049 .idle_timeout
1050 .and_then(|duration| entry.idle_since.checked_add(duration));
1051 let lifetime = options
1052 .max_lifetime
1053 .and_then(|duration| entry.created_at.checked_add(duration));
1054 match (idle, lifetime) {
1055 (Some(idle), Some(lifetime)) => Some(idle.min(lifetime)),
1056 (Some(deadline), None) | (None, Some(deadline)) => Some(deadline),
1057 (None, None) => None,
1058 }
1059}
1060
1061#[cfg(test)]
1062#[path = "../tests/pool.rs"]
1063mod tests;
1064
1065#[cfg(test)]
1066#[path = "../tests/cancellation.rs"]
1067mod cancellation_tests;