Skip to content

Commit 727b747

Browse files
committed
fix(client): schedule interval to clear expired idle connections
Currently only works if Client is built with a `Handle`, and not a custome executor, since a `Handle` is required to create a tokio Interval.
1 parent 1223fc2 commit 727b747

File tree

2 files changed

+37
-23
lines changed

2 files changed

+37
-23
lines changed

src/client/mod.rs

+15-19
Original file line numberDiff line numberDiff line change
@@ -90,33 +90,15 @@ impl<C, B> Client<C, B> {
9090
Exec::Executor(..) => panic!("Client not built with a Handle"),
9191
}
9292
}
93-
}
94-
9593

96-
impl<C, B> Client<C, B>
97-
where C: Connect,
98-
B: Stream<Error=::Error>,
99-
B::Item: AsRef<[u8]>,
100-
{
101-
// Create a new client with a specific connector.
10294
#[inline]
10395
fn configured(config: Config<C, B>, exec: Exec) -> Client<C, B> {
104-
let client = Client {
96+
Client {
10597
connector: Rc::new(config.connector),
10698
executor: exec,
10799
h1_writev: config.h1_writev,
108100
pool: Pool::new(config.keep_alive, config.keep_alive_timeout),
109101
retry_canceled_requests: config.retry_canceled_requests,
110-
};
111-
112-
client.schedule_pool_timer();
113-
114-
client
115-
}
116-
117-
fn schedule_pool_timer(&self) {
118-
if let Exec::Handle(ref h) = self.executor {
119-
self.pool.spawn_expired_interval(h);
120102
}
121103
}
122104
}
@@ -135,6 +117,14 @@ where C: Connect,
135117
/// Send a constructed Request using this Client.
136118
#[inline]
137119
pub fn request(&self, mut req: Request<B>) -> FutureResponse {
120+
// TODO(0.12): do this at construction time.
121+
//
122+
// It cannot be done in the constructor because the Client::configured
123+
// does not have `B: 'static` bounds, which are required to spawn
124+
// the interval. In 0.12, add a static bounds to the constructor,
125+
// and move this.
126+
self.schedule_pool_timer();
127+
138128
match req.version() {
139129
HttpVersion::Http10 |
140130
HttpVersion::Http11 => (),
@@ -263,6 +253,12 @@ where C: Connect,
263253

264254
Box::new(resp)
265255
}
256+
257+
fn schedule_pool_timer(&self) {
258+
if let Exec::Handle(ref h) = self.executor {
259+
self.pool.spawn_expired_interval(h);
260+
}
261+
}
266262
}
267263

268264
impl<C, B> Service for Client<C, B>

src/client/pool.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ struct PoolInner<T> {
4141
// connection.
4242
parked: HashMap<Rc<String>, VecDeque<relay::Sender<Entry<T>>>>,
4343
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,
4450
}
4551

4652
impl<T: Clone + Ready> Pool<T> {
@@ -51,6 +57,7 @@ impl<T: Clone + Ready> Pool<T> {
5157
idle: HashMap::new(),
5258
parked: HashMap::new(),
5359
timeout: timeout,
60+
expired_timer_spawned: false,
5461
})),
5562
}
5663
}
@@ -229,12 +236,17 @@ impl<T> Pool<T> {
229236

230237
impl<T: 'static> Pool<T> {
231238
pub(super) fn spawn_expired_interval(&self, handle: &Handle) {
232-
let inner = self.inner.borrow();
239+
let mut inner = self.inner.borrow_mut();
233240

234241
if !inner.enabled {
235242
return;
236243
}
237244

245+
if inner.expired_timer_spawned {
246+
return;
247+
}
248+
inner.expired_timer_spawned = true;
249+
238250
let dur = if let Some(dur) = inner.timeout {
239251
dur
240252
} else {
@@ -529,7 +541,9 @@ mod tests {
529541

530542
#[test]
531543
fn test_pool_timer_removes_expired() {
532-
let pool = Pool::new(true, Some(Duration::from_secs(1)));
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());
533547
let key = Rc::new("foo".to_string());
534548

535549
let mut pooled1 = pool.pooled(key.clone(), 41);
@@ -540,9 +554,13 @@ mod tests {
540554
pooled3.idle();
541555

542556
assert_eq!(pool.inner.borrow().idle.get(&key).map(|entries| entries.len()), Some(3));
543-
::std::thread::sleep(pool.inner.borrow().timeout.unwrap());
544557

545-
pool.clear_expired();
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+
546564
assert!(pool.inner.borrow().idle.get(&key).is_none());
547565
}
548566

0 commit comments

Comments
 (0)