|
|
|
|
@@ -37,7 +37,6 @@ pub(super) enum Reservation<T> {
|
|
|
|
|
/// This connection could be used multiple times, the first one will be
|
|
|
|
|
/// reinserted into the `idle` pool, and the second will be given to
|
|
|
|
|
/// the `Checkout`.
|
|
|
|
|
#[allow(unused)]
|
|
|
|
|
Shared(T, T),
|
|
|
|
|
/// This connection requires unique access. It will be returned after
|
|
|
|
|
/// use is complete.
|
|
|
|
|
@@ -65,7 +64,7 @@ struct PoolInner<T> {
|
|
|
|
|
// this list is checked for any parked Checkouts, and tries to notify
|
|
|
|
|
// them that the Conn could be used instead of waiting for a brand new
|
|
|
|
|
// connection.
|
|
|
|
|
parked: HashMap<Key, VecDeque<oneshot::Sender<T>>>,
|
|
|
|
|
waiters: HashMap<Key, VecDeque<oneshot::Sender<T>>>,
|
|
|
|
|
timeout: Option<Duration>,
|
|
|
|
|
// A oneshot channel is used to allow the interval to be notified when
|
|
|
|
|
// the Pool completely drops. That way, the interval can cancel immediately.
|
|
|
|
|
@@ -80,7 +79,7 @@ impl<T> Pool<T> {
|
|
|
|
|
enabled: enabled,
|
|
|
|
|
idle: HashMap::new(),
|
|
|
|
|
idle_interval_ref: None,
|
|
|
|
|
parked: HashMap::new(),
|
|
|
|
|
waiters: HashMap::new(),
|
|
|
|
|
timeout: timeout,
|
|
|
|
|
})),
|
|
|
|
|
}
|
|
|
|
|
@@ -94,7 +93,7 @@ impl<T: Poolable> Pool<T> {
|
|
|
|
|
Checkout {
|
|
|
|
|
key,
|
|
|
|
|
pool: self.clone(),
|
|
|
|
|
parked: None,
|
|
|
|
|
waiter: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -217,10 +216,10 @@ impl<T: Poolable> Pool<T> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn park(&mut self, key: Key, tx: oneshot::Sender<T>) {
|
|
|
|
|
fn waiter(&mut self, key: Key, tx: oneshot::Sender<T>) {
|
|
|
|
|
trace!("checkout waiting for idle connection: {:?}", key);
|
|
|
|
|
self.inner.lock().unwrap()
|
|
|
|
|
.parked.entry(key)
|
|
|
|
|
.waiters.entry(key)
|
|
|
|
|
.or_insert(VecDeque::new())
|
|
|
|
|
.push_back(tx);
|
|
|
|
|
}
|
|
|
|
|
@@ -285,10 +284,10 @@ impl<T: Poolable> PoolInner<T> {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
trace!("put; add idle connection for {:?}", key);
|
|
|
|
|
let mut remove_parked = false;
|
|
|
|
|
let mut remove_waiters = false;
|
|
|
|
|
let mut value = Some(value);
|
|
|
|
|
if let Some(parked) = self.parked.get_mut(&key) {
|
|
|
|
|
while let Some(tx) = parked.pop_front() {
|
|
|
|
|
if let Some(waiters) = self.waiters.get_mut(&key) {
|
|
|
|
|
while let Some(tx) = waiters.pop_front() {
|
|
|
|
|
if !tx.is_canceled() {
|
|
|
|
|
let reserved = value.take().expect("value already sent");
|
|
|
|
|
let reserved = match reserved.reserve() {
|
|
|
|
|
@@ -314,10 +313,10 @@ impl<T: Poolable> PoolInner<T> {
|
|
|
|
|
|
|
|
|
|
trace!("put; removing canceled waiter for {:?}", key);
|
|
|
|
|
}
|
|
|
|
|
remove_parked = parked.is_empty();
|
|
|
|
|
remove_waiters = waiters.is_empty();
|
|
|
|
|
}
|
|
|
|
|
if remove_parked {
|
|
|
|
|
self.parked.remove(&key);
|
|
|
|
|
if remove_waiters {
|
|
|
|
|
self.waiters.remove(&key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match value {
|
|
|
|
|
@@ -345,7 +344,7 @@ impl<T: Poolable> PoolInner<T> {
|
|
|
|
|
// cancel any waiters. if there are any, it's because
|
|
|
|
|
// this Connecting task didn't complete successfully.
|
|
|
|
|
// those waiters would never receive a connection.
|
|
|
|
|
self.parked.remove(key);
|
|
|
|
|
self.waiters.remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -354,16 +353,16 @@ impl<T> PoolInner<T> {
|
|
|
|
|
/// and possibly inserted into the pool that it is waiting for an idle
|
|
|
|
|
/// connection. If a user ever dropped that future, we need to clean out
|
|
|
|
|
/// those parked senders.
|
|
|
|
|
fn clean_parked(&mut self, key: &Key) {
|
|
|
|
|
let mut remove_parked = false;
|
|
|
|
|
if let Some(parked) = self.parked.get_mut(key) {
|
|
|
|
|
parked.retain(|tx| {
|
|
|
|
|
fn clean_waiters(&mut self, key: &Key) {
|
|
|
|
|
let mut remove_waiters = false;
|
|
|
|
|
if let Some(waiters) = self.waiters.get_mut(key) {
|
|
|
|
|
waiters.retain(|tx| {
|
|
|
|
|
!tx.is_canceled()
|
|
|
|
|
});
|
|
|
|
|
remove_parked = parked.is_empty();
|
|
|
|
|
remove_waiters = waiters.is_empty();
|
|
|
|
|
}
|
|
|
|
|
if remove_parked {
|
|
|
|
|
self.parked.remove(key);
|
|
|
|
|
if remove_waiters {
|
|
|
|
|
self.waiters.remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -511,13 +510,13 @@ struct Idle<T> {
|
|
|
|
|
pub(super) struct Checkout<T> {
|
|
|
|
|
key: Key,
|
|
|
|
|
pool: Pool<T>,
|
|
|
|
|
parked: Option<oneshot::Receiver<T>>,
|
|
|
|
|
waiter: Option<oneshot::Receiver<T>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Poolable> Checkout<T> {
|
|
|
|
|
fn poll_parked(&mut self) -> Poll<Option<Pooled<T>>, ::Error> {
|
|
|
|
|
fn poll_waiter(&mut self) -> Poll<Option<Pooled<T>>, ::Error> {
|
|
|
|
|
static CANCELED: &str = "pool checkout failed";
|
|
|
|
|
if let Some(ref mut rx) = self.parked {
|
|
|
|
|
if let Some(mut rx) = self.waiter.take() {
|
|
|
|
|
match rx.poll() {
|
|
|
|
|
Ok(Async::Ready(value)) => {
|
|
|
|
|
if !value.is_closed() {
|
|
|
|
|
@@ -526,7 +525,10 @@ impl<T: Poolable> Checkout<T> {
|
|
|
|
|
Err(::Error::new_canceled(Some(CANCELED)))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Ok(Async::NotReady) => Ok(Async::NotReady),
|
|
|
|
|
Ok(Async::NotReady) => {
|
|
|
|
|
self.waiter = Some(rx);
|
|
|
|
|
Ok(Async::NotReady)
|
|
|
|
|
},
|
|
|
|
|
Err(_canceled) => Err(::Error::new_canceled(Some(CANCELED))),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
@@ -534,12 +536,12 @@ impl<T: Poolable> Checkout<T> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn park(&mut self) {
|
|
|
|
|
if self.parked.is_none() {
|
|
|
|
|
fn add_waiter(&mut self) {
|
|
|
|
|
if self.waiter.is_none() {
|
|
|
|
|
let (tx, mut rx) = oneshot::channel();
|
|
|
|
|
let _ = rx.poll(); // park this task
|
|
|
|
|
self.pool.park(self.key.clone(), tx);
|
|
|
|
|
self.parked = Some(rx);
|
|
|
|
|
self.pool.waiter(self.key.clone(), tx);
|
|
|
|
|
self.waiter = Some(rx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -549,7 +551,7 @@ impl<T: Poolable> Future for Checkout<T> {
|
|
|
|
|
type Error = ::Error;
|
|
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
|
|
|
|
if let Some(pooled) = try_ready!(self.poll_parked()) {
|
|
|
|
|
if let Some(pooled) = try_ready!(self.poll_waiter()) {
|
|
|
|
|
return Ok(Async::Ready(pooled));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -558,7 +560,7 @@ impl<T: Poolable> Future for Checkout<T> {
|
|
|
|
|
if let Some(pooled) = entry {
|
|
|
|
|
Ok(Async::Ready(pooled))
|
|
|
|
|
} else {
|
|
|
|
|
self.park();
|
|
|
|
|
self.add_waiter();
|
|
|
|
|
Ok(Async::NotReady)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -566,9 +568,10 @@ impl<T: Poolable> Future for Checkout<T> {
|
|
|
|
|
|
|
|
|
|
impl<T> Drop for Checkout<T> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.parked.take();
|
|
|
|
|
if let Ok(mut inner) = self.pool.inner.lock() {
|
|
|
|
|
inner.clean_parked(&self.key);
|
|
|
|
|
if self.waiter.take().is_some() {
|
|
|
|
|
if let Ok(mut inner) = self.pool.inner.lock() {
|
|
|
|
|
inner.clean_waiters(&self.key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -782,7 +785,7 @@ mod tests {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_pool_checkout_drop_cleans_up_parked() {
|
|
|
|
|
fn test_pool_checkout_drop_cleans_up_waiters() {
|
|
|
|
|
future::lazy(|| {
|
|
|
|
|
let pool = Pool::<Uniq<i32>>::new(true, Some(Duration::from_secs(10)));
|
|
|
|
|
let key = (Arc::new("localhost:12345".to_string()), Ver::Http1);
|
|
|
|
|
@@ -792,16 +795,16 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
// first poll needed to get into Pool's parked
|
|
|
|
|
checkout1.poll().unwrap();
|
|
|
|
|
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 1);
|
|
|
|
|
assert_eq!(pool.inner.lock().unwrap().waiters.get(&key).unwrap().len(), 1);
|
|
|
|
|
checkout2.poll().unwrap();
|
|
|
|
|
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 2);
|
|
|
|
|
assert_eq!(pool.inner.lock().unwrap().waiters.get(&key).unwrap().len(), 2);
|
|
|
|
|
|
|
|
|
|
// on drop, clean up Pool
|
|
|
|
|
drop(checkout1);
|
|
|
|
|
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 1);
|
|
|
|
|
assert_eq!(pool.inner.lock().unwrap().waiters.get(&key).unwrap().len(), 1);
|
|
|
|
|
|
|
|
|
|
drop(checkout2);
|
|
|
|
|
assert!(pool.inner.lock().unwrap().parked.get(&key).is_none());
|
|
|
|
|
assert!(pool.inner.lock().unwrap().waiters.get(&key).is_none());
|
|
|
|
|
|
|
|
|
|
::futures::future::ok::<(), ()>(())
|
|
|
|
|
}).wait().unwrap();
|
|
|
|
|
|