Skip to content

Commit ccec79d

Browse files
committed
fix(client): prevent pool checkout looping on not-ready connections
Closes #1519
1 parent 2c48101 commit ccec79d

File tree

7 files changed

+31
-214
lines changed

7 files changed

+31
-214
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ tokio-proto = { version = "0.1", optional = true }
3838
tokio-service = "0.1"
3939
tokio-io = "0.1"
4040
unicase = "2.0"
41+
want = "0.0.4"
4142

4243
[dev-dependencies]
4344
num_cpus = "1.0"

src/client/conn.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,6 @@ impl<B> SendRequest<B>
129129
pub(super) fn is_ready(&self) -> bool {
130130
self.dispatch.is_ready()
131131
}
132-
133-
pub(super) fn is_closed(&self) -> bool {
134-
self.dispatch.is_closed()
135-
}
136132
}
137133

138134
impl<B> SendRequest<B>

src/client/dispatch.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use futures::{Async, Poll, Stream};
22
use futures::sync::{mpsc, oneshot};
3+
use want;
34

45
use common::Never;
5-
use super::signal;
66

77
//pub type Callback<T, U> = oneshot::Sender<Result<U, (::Error, Option<T>)>>;
88
pub type RetryPromise<T, U> = oneshot::Receiver<Result<U, (::Error, Option<T>)>>;
99
pub type Promise<T> = oneshot::Receiver<Result<T, ::Error>>;
1010

1111
pub fn channel<T, U>() -> (Sender<T, U>, Receiver<T, U>) {
1212
let (tx, rx) = mpsc::channel(0);
13-
let (giver, taker) = signal::new();
13+
let (giver, taker) = want::new();
1414
let tx = Sender {
1515
giver: giver,
1616
inner: tx,
@@ -27,7 +27,7 @@ pub struct Sender<T, U> {
2727
// when the queue is empty. This helps us know when a request and
2828
// response have been fully processed, and a connection is ready
2929
// for more.
30-
giver: signal::Giver,
30+
giver: want::Giver,
3131
inner: mpsc::Sender<(T, Callback<T, U>)>,
3232
}
3333

@@ -49,18 +49,16 @@ impl<T, U> Sender<T, U> {
4949
self.giver.is_wanting()
5050
}
5151

52-
pub fn is_closed(&self) -> bool {
53-
self.giver.is_canceled()
54-
}
55-
5652
pub fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
53+
self.giver.give();
5754
let (tx, rx) = oneshot::channel();
5855
self.inner.try_send((val, Callback::Retry(tx)))
5956
.map(move |_| rx)
6057
.map_err(|e| e.into_inner().0)
6158
}
6259

6360
pub fn send(&mut self, val: T) -> Result<Promise<U>, T> {
61+
self.giver.give();
6462
let (tx, rx) = oneshot::channel();
6563
self.inner.try_send((val, Callback::NoRetry(tx)))
6664
.map(move |_| rx)
@@ -70,7 +68,7 @@ impl<T, U> Sender<T, U> {
7068

7169
pub struct Receiver<T, U> {
7270
inner: mpsc::Receiver<(T, Callback<T, U>)>,
73-
taker: signal::Taker,
71+
taker: want::Taker,
7472
}
7573

7674
impl<T, U> Stream for Receiver<T, U> {

src/client/mod.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ mod dns;
3737
mod pool;
3838
#[cfg(feature = "compat")]
3939
pub mod compat;
40-
mod signal;
4140
#[cfg(test)]
4241
mod tests;
4342

@@ -262,13 +261,22 @@ where C: Connect,
262261
// If the executor doesn't have room, oh well. Things will likely
263262
// be blowing up soon, but this specific task isn't required.
264263
let _ = executor.execute(future::poll_fn(move || {
265-
pooled.tx.poll_ready().map_err(|_| ())
264+
pooled.tx.poll_ready()
266265
}).then(move |_| {
267266
// At this point, `pooled` is dropped, and had a chance
268267
// to insert into the pool (if conn was idle)
269268
drop(delayed_tx);
270269
Ok(())
271270
}));
271+
} else {
272+
// There's no body to delay, but the connection isn't
273+
// ready yet. Only re-insert when it's ready...
274+
let _ = executor.execute(
275+
future::poll_fn(move || {
276+
pooled.tx.poll_ready()
277+
})
278+
.then(|_| Ok(()))
279+
);
272280
}
273281

274282
res
@@ -395,8 +403,8 @@ impl<B> self::pool::Closed for PoolClient<B>
395403
where
396404
B: 'static,
397405
{
398-
fn is_closed(&self) -> bool {
399-
self.tx.is_closed()
406+
fn is_open(&self) -> bool {
407+
self.tx.is_ready()
400408
}
401409
}
402410

src/client/pool.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub struct Pool<T> {
1818
//
1919
// See https://github.com/hyperium/hyper/issues/1429
2020
pub trait Closed {
21-
fn is_closed(&self) -> bool;
21+
fn is_open(&self) -> bool;
2222
}
2323

2424
struct PoolInner<T> {
@@ -77,7 +77,7 @@ impl<T: Closed> Pool<T> {
7777
trace!("take; url = {:?}, expiration = {:?}", key, expiration.0);
7878
while let Some(entry) = list.pop() {
7979
if !expiration.expires(entry.idle_at) {
80-
if !entry.value.is_closed() {
80+
if entry.value.is_open() {
8181
should_remove = list.is_empty();
8282
return Some(entry);
8383
}
@@ -205,7 +205,7 @@ impl<T: Closed> PoolInner<T> {
205205
self.idle.retain(|_key, values| {
206206

207207
values.retain(|entry| {
208-
if entry.value.is_closed() {
208+
if !entry.value.is_open() {
209209
return false;
210210
}
211211
now - entry.idle_at < dur
@@ -293,6 +293,11 @@ impl<T: Closed> DerefMut for Pooled<T> {
293293
impl<T: Closed> Drop for Pooled<T> {
294294
fn drop(&mut self) {
295295
if let Some(value) = self.value.take() {
296+
if !value.is_open() {
297+
// don't ever re-insert not-open connections back
298+
// into the pool!
299+
return;
300+
}
296301
if let Some(inner) = self.pool.upgrade() {
297302
if let Ok(mut inner) = inner.lock() {
298303
inner.put(self.key.clone(), value);
@@ -331,7 +336,7 @@ impl<T: Closed> Checkout<T> {
331336
if let Some(ref mut rx) = self.parked {
332337
match rx.poll() {
333338
Ok(Async::Ready(value)) => {
334-
if !value.is_closed() {
339+
if value.is_open() {
335340
return Ok(Async::Ready(self.pool.reuse(&self.key, value)));
336341
}
337342
drop_parked = true;
@@ -434,8 +439,8 @@ mod tests {
434439
use super::{Closed, Pool};
435440

436441
impl Closed for i32 {
437-
fn is_closed(&self) -> bool {
438-
false
442+
fn is_open(&self) -> bool {
443+
true
439444
}
440445
}
441446

src/client/signal.rs

Lines changed: 0 additions & 192 deletions
This file was deleted.

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ extern crate tokio_core as tokio;
3838
extern crate tokio_proto;
3939
extern crate tokio_service;
4040
extern crate unicase;
41+
extern crate want;
4142

4243
#[cfg(all(test, feature = "nightly"))]
4344
extern crate test;

0 commit comments

Comments
 (0)