@@ -6,7 +6,8 @@ use std::ops::{Deref, DerefMut, BitAndAssign};
6
6
use std:: rc:: { Rc , Weak } ;
7
7
use std:: time:: { Duration , Instant } ;
8
8
9
- use futures:: { Future , Async , Poll } ;
9
+ use futures:: { Future , Async , Poll , Stream } ;
10
+ use tokio:: reactor:: { Handle , Interval } ;
10
11
use relay;
11
12
12
13
use proto:: { KeepAlive , KA } ;
@@ -40,6 +41,12 @@ struct PoolInner<T> {
40
41
// connection.
41
42
parked : HashMap < Rc < String > , VecDeque < relay:: Sender < Entry < T > > > > ,
42
43
timeout : Option < Duration > ,
44
+ // Used to prevent multiple intervals from being spawned to clear
45
+ // expired connections.
46
+ //
47
+ // TODO(0.12): Remove the need for this when Client::schedule_pool_timer
48
+ // can be done in Client::new.
49
+ expired_timer_spawned : bool ,
43
50
}
44
51
45
52
impl < T : Clone + Ready > Pool < T > {
@@ -50,6 +57,7 @@ impl<T: Clone + Ready> Pool<T> {
50
57
idle : HashMap :: new ( ) ,
51
58
parked : HashMap :: new ( ) ,
52
59
timeout : timeout,
60
+ expired_timer_spawned : false ,
53
61
} ) ) ,
54
62
}
55
63
}
@@ -194,6 +202,64 @@ impl<T> Pool<T> {
194
202
inner. parked . remove ( key) ;
195
203
}
196
204
}
205
+
206
+ fn clear_expired ( & self ) {
207
+ let mut inner = self . inner . borrow_mut ( ) ;
208
+
209
+ let dur = if let Some ( dur) = inner. timeout {
210
+ dur
211
+ } else {
212
+ return
213
+ } ;
214
+
215
+ let now = Instant :: now ( ) ;
216
+ //self.last_idle_check_at = now;
217
+
218
+ inner. idle . retain ( |_key, values| {
219
+
220
+ values. retain ( |val| {
221
+ match val. status . get ( ) {
222
+ TimedKA :: Idle ( idle_at) if now - idle_at < dur => {
223
+ true
224
+ } ,
225
+ _ => false ,
226
+ }
227
+ //now - val.idle_at < dur
228
+ } ) ;
229
+
230
+ // returning false evicts this key/val
231
+ !values. is_empty ( )
232
+ } ) ;
233
+ }
234
+ }
235
+
236
+
237
+ impl < T : ' static > Pool < T > {
238
+ pub ( super ) fn spawn_expired_interval ( & self , handle : & Handle ) {
239
+ let mut inner = self . inner . borrow_mut ( ) ;
240
+
241
+ if !inner. enabled {
242
+ return ;
243
+ }
244
+
245
+ if inner. expired_timer_spawned {
246
+ return ;
247
+ }
248
+ inner. expired_timer_spawned = true ;
249
+
250
+ let dur = if let Some ( dur) = inner. timeout {
251
+ dur
252
+ } else {
253
+ return
254
+ } ;
255
+
256
+ let interval = Interval :: new ( dur, handle)
257
+ . expect ( "reactor is gone" ) ;
258
+ handle. spawn ( IdleInterval {
259
+ interval : interval,
260
+ pool : Rc :: downgrade ( & self . inner ) ,
261
+ } ) ;
262
+ }
197
263
}
198
264
199
265
impl < T > Clone for Pool < T > {
@@ -385,6 +451,28 @@ impl Expiration {
385
451
}
386
452
}
387
453
454
+ struct IdleInterval < T > {
455
+ interval : Interval ,
456
+ pool : Weak < RefCell < PoolInner < T > > > ,
457
+ }
458
+
459
+ impl < T : ' static > Future for IdleInterval < T > {
460
+ type Item = ( ) ;
461
+ type Error = ( ) ;
462
+
463
+ fn poll ( & mut self ) -> Poll < Self :: Item , Self :: Error > {
464
+ loop {
465
+ try_ready ! ( self . interval. poll( ) . map_err( |_| unreachable!( "interval cannot error" ) ) ) ;
466
+
467
+ if let Some ( inner) = self . pool . upgrade ( ) {
468
+ let pool = Pool { inner : inner } ;
469
+ pool. clear_expired ( ) ;
470
+ } else {
471
+ return Ok ( Async :: Ready ( ( ) ) ) ;
472
+ }
473
+ }
474
+ }
475
+ }
388
476
389
477
#[ cfg( test) ]
390
478
mod tests {
@@ -428,7 +516,7 @@ mod tests {
428
516
}
429
517
430
518
#[ test]
431
- fn test_pool_removes_expired ( ) {
519
+ fn test_pool_checkout_removes_expired ( ) {
432
520
let pool = Pool :: new ( true , Some ( Duration :: from_secs ( 1 ) ) ) ;
433
521
let key = Rc :: new ( "foo" . to_string ( ) ) ;
434
522
@@ -451,6 +539,31 @@ mod tests {
451
539
assert ! ( pool. inner. borrow( ) . idle. get( & key) . is_none( ) ) ;
452
540
}
453
541
542
+ #[ test]
543
+ fn test_pool_timer_removes_expired ( ) {
544
+ let mut core = :: tokio:: reactor:: Core :: new ( ) . unwrap ( ) ;
545
+ let pool = Pool :: new ( true , Some ( Duration :: from_millis ( 100 ) ) ) ;
546
+ pool. spawn_expired_interval ( & core. handle ( ) ) ;
547
+ let key = Rc :: new ( "foo" . to_string ( ) ) ;
548
+
549
+ let mut pooled1 = pool. pooled ( key. clone ( ) , 41 ) ;
550
+ pooled1. idle ( ) ;
551
+ let mut pooled2 = pool. pooled ( key. clone ( ) , 5 ) ;
552
+ pooled2. idle ( ) ;
553
+ let mut pooled3 = pool. pooled ( key. clone ( ) , 99 ) ;
554
+ pooled3. idle ( ) ;
555
+
556
+ assert_eq ! ( pool. inner. borrow( ) . idle. get( & key) . map( |entries| entries. len( ) ) , Some ( 3 ) ) ;
557
+
558
+ let timeout = :: tokio:: reactor:: Timeout :: new (
559
+ Duration :: from_millis ( 400 ) , // allow for too-good resolution
560
+ & core. handle ( )
561
+ ) . unwrap ( ) ;
562
+ core. run ( timeout) . unwrap ( ) ;
563
+
564
+ assert ! ( pool. inner. borrow( ) . idle. get( & key) . is_none( ) ) ;
565
+ }
566
+
454
567
#[ test]
455
568
fn test_pool_checkout_task_unparked ( ) {
456
569
let pool = Pool :: new ( true , Some ( Duration :: from_secs ( 10 ) ) ) ;
0 commit comments