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