@@ -81,9 +81,13 @@ impl<DB: Database> PoolInner<DB> {
81
81
self . is_closed . load ( Ordering :: Acquire )
82
82
}
83
83
84
- pub ( super ) fn close < ' a > ( self : & ' a Arc < Self > ) -> impl Future < Output = ( ) > + ' a {
84
+ fn mark_closed ( & self ) {
85
85
self . is_closed . store ( true , Ordering :: Release ) ;
86
86
self . on_closed . notify ( usize:: MAX ) ;
87
+ }
88
+
89
+ pub ( super ) fn close < ' a > ( self : & ' a Arc < Self > ) -> impl Future < Output = ( ) > + ' a {
90
+ self . mark_closed ( ) ;
87
91
88
92
async move {
89
93
for permits in 1 ..=self . options . max_connections {
@@ -209,19 +213,25 @@ impl<DB: Database> PoolInner<DB> {
209
213
}
210
214
211
215
/// Try to atomically increment the pool size for a new connection.
216
+ ///
217
+ /// Returns `Err` if the pool is at max capacity already or is closed.
212
218
pub ( super ) fn try_increment_size < ' a > (
213
219
self : & ' a Arc < Self > ,
214
220
permit : AsyncSemaphoreReleaser < ' a > ,
215
221
) -> Result < DecrementSizeGuard < DB > , AsyncSemaphoreReleaser < ' a > > {
216
222
match self
217
223
. size
218
224
. fetch_update ( Ordering :: AcqRel , Ordering :: Acquire , |size| {
225
+ if self . is_closed ( ) {
226
+ return None ;
227
+ }
228
+
219
229
size. checked_add ( 1 )
220
230
. filter ( |size| size <= & self . options . max_connections )
221
231
} ) {
222
232
// we successfully incremented the size
223
233
Ok ( _) => Ok ( DecrementSizeGuard :: from_permit ( ( * self ) . clone ( ) , permit) ) ,
224
- // the pool is at max capacity
234
+ // the pool is at max capacity or is closed
225
235
Err ( _) => Err ( permit) ,
226
236
}
227
237
}
@@ -258,7 +268,9 @@ impl<DB: Database> PoolInner<DB> {
258
268
// we can open a new connection
259
269
guard
260
270
} else {
261
- // This can happen for a child pool that's at its connection limit.
271
+ // This can happen for a child pool that's at its connection limit,
272
+ // or if the pool was closed between `acquire_permit()` and
273
+ // `try_increment_size()`.
262
274
tracing:: debug!( "woke but was unable to acquire idle connection or open new one; retrying" ) ;
263
275
// If so, we're likely in the current-thread runtime if it's Tokio
264
276
// and so we should yield to let any spawned release_to_pool() tasks
@@ -395,6 +407,8 @@ impl<DB: Database> PoolInner<DB> {
395
407
396
408
impl < DB : Database > Drop for PoolInner < DB > {
397
409
fn drop ( & mut self ) {
410
+ self . mark_closed ( ) ;
411
+
398
412
if let Some ( parent) = & self . options . parent_pool {
399
413
// Release the stolen permits.
400
414
parent. 0 . semaphore . release ( self . semaphore . permits ( ) ) ;
@@ -461,7 +475,9 @@ async fn check_idle_conn<DB: Database>(
461
475
}
462
476
463
477
fn spawn_maintenance_tasks < DB : Database > ( pool : & Arc < PoolInner < DB > > ) {
464
- let pool = Arc :: clone ( & pool) ;
478
+ // NOTE: use `pool_weak` for the maintenance tasks so
479
+ // they don't keep `PoolInner` from being dropped.
480
+ let pool_weak = Arc :: downgrade ( & pool) ;
465
481
466
482
let period = match ( pool. options . max_lifetime , pool. options . idle_timeout ) {
467
483
( Some ( it) , None ) | ( None , Some ( it) ) => it,
@@ -471,35 +487,51 @@ fn spawn_maintenance_tasks<DB: Database>(pool: &Arc<PoolInner<DB>>) {
471
487
( None , None ) => {
472
488
if pool. options . min_connections > 0 {
473
489
crate :: rt:: spawn ( async move {
474
- pool. min_connections_maintenance ( None ) . await ;
490
+ if let Some ( pool) = pool_weak. upgrade ( ) {
491
+ pool. min_connections_maintenance ( None ) . await ;
492
+ }
475
493
} ) ;
476
494
}
477
495
478
496
return ;
479
497
}
480
498
} ;
481
499
500
+ // Immediately cancel this task if the pool is closed.
501
+ let mut close_event = pool. close_event ( ) ;
502
+
482
503
crate :: rt:: spawn ( async move {
483
- // Immediately cancel this task if the pool is closed.
484
- let _ = pool
485
- . close_event ( )
504
+ let _ = close_event
486
505
. do_until ( async {
487
- while !pool. is_closed ( ) {
506
+ let mut slept = true ;
507
+
508
+ // If the last handle to the pool was dropped while we were sleeping
509
+ while let Some ( pool) = pool_weak. upgrade ( ) {
510
+ if pool. is_closed ( ) {
511
+ return ;
512
+ }
513
+
514
+ // Don't run the reaper right away.
515
+ if slept && !pool. idle_conns . is_empty ( ) {
516
+ do_reap ( & pool) . await ;
517
+ }
518
+
488
519
let next_run = Instant :: now ( ) + period;
489
520
490
521
pool. min_connections_maintenance ( Some ( next_run) ) . await ;
491
522
523
+ // Don't hold a reference to the pool while sleeping.
524
+ drop ( pool) ;
525
+
492
526
if let Some ( duration) = next_run. checked_duration_since ( Instant :: now ( ) ) {
493
527
// `async-std` doesn't have a `sleep_until()`
494
528
crate :: rt:: sleep ( duration) . await ;
495
529
} else {
530
+ // `next_run` is in the past, just yield.
496
531
crate :: rt:: yield_now ( ) . await ;
497
532
}
498
533
499
- // Don't run the reaper right away.
500
- if !pool. idle_conns . is_empty ( ) {
501
- do_reap ( & pool) . await ;
502
- }
534
+ slept = true ;
503
535
}
504
536
} )
505
537
. await ;
0 commit comments