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