Skip to content

Commit fd12d9c

Browse files
author
Ariel Ben-Yehuda
committed
fix: support fake time
This avoids bad behavior like being stuck on a busy loop when tokio paused clock is used. Requires hyperium/hyper#3965
1 parent 3c8dbe4 commit fd12d9c

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

src/client/legacy/pool.rs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ struct IdlePopper<'a, T, K> {
298298
}
299299

300300
impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
301-
fn pop(self, expiration: &Expiration) -> Option<Idle<T>> {
301+
fn pop(self, expiration: &Expiration, now: Instant) -> Option<Idle<T>> {
302302
while let Some(entry) = self.list.pop() {
303303
// If the connection has been closed, or is older than our idle
304304
// timeout, simply drop it and keep looking...
@@ -312,7 +312,7 @@ impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
312312
//
313313
// In that case, we could just break out of the loop and drop the
314314
// whole list...
315-
if expiration.expires(entry.idle_at) {
315+
if expiration.expires(entry.idle_at, now) {
316316
trace!("removing expired connection for {:?}", self.key);
317317
continue;
318318
}
@@ -321,7 +321,7 @@ impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
321321
#[cfg(feature = "http2")]
322322
Reservation::Shared(to_reinsert, to_checkout) => {
323323
self.list.push(Idle {
324-
idle_at: Instant::now(),
324+
idle_at: now,
325325
value: to_reinsert,
326326
});
327327
to_checkout
@@ -340,6 +340,12 @@ impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
340340
}
341341

342342
impl<T: Poolable, K: Key> PoolInner<T, K> {
343+
fn now(&self) -> Instant {
344+
self.timer
345+
.as_ref()
346+
.map_or_else(|| Instant::now(), |t| t.now())
347+
}
348+
343349
fn put(&mut self, key: K, value: T, __pool_ref: &Arc<Mutex<PoolInner<T, K>>>) {
344350
if value.can_share() && self.idle.contains_key(&key) {
345351
trace!("put; existing idle HTTP/2 connection for {:?}", key);
@@ -386,6 +392,7 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
386392
Some(value) => {
387393
// borrow-check scope...
388394
{
395+
let now = self.now();
389396
let idle_list = self.idle.entry(key.clone()).or_default();
390397
if self.max_idle_per_host <= idle_list.len() {
391398
trace!("max idle per host for {:?}, dropping connection", key);
@@ -395,7 +402,7 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
395402
debug!("pooling idle connection for {:?}", key);
396403
idle_list.push(Idle {
397404
value,
398-
idle_at: Instant::now(),
405+
idle_at: now,
399406
});
400407
}
401408

@@ -477,7 +484,7 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
477484
fn clear_expired(&mut self) {
478485
let dur = self.timeout.expect("interval assumes timeout");
479486

480-
let now = Instant::now();
487+
let now = self.now();
481488
//self.last_idle_check_at = now;
482489

483490
self.idle.retain(|key, values| {
@@ -649,6 +656,7 @@ impl<T: Poolable, K: Key> Checkout<T, K> {
649656
let entry = {
650657
let mut inner = self.pool.inner.as_ref()?.lock().unwrap();
651658
let expiration = Expiration::new(inner.timeout);
659+
let now = inner.now();
652660
let maybe_entry = inner.idle.get_mut(&self.key).and_then(|list| {
653661
trace!("take? {:?}: expiration = {:?}", self.key, expiration.0);
654662
// A block to end the mutable borrow on list,
@@ -658,7 +666,7 @@ impl<T: Poolable, K: Key> Checkout<T, K> {
658666
key: &self.key,
659667
list,
660668
};
661-
popper.pop(&expiration)
669+
popper.pop(&expiration, now)
662670
}
663671
.map(|e| (e, list.is_empty()))
664672
});
@@ -762,10 +770,10 @@ impl Expiration {
762770
Expiration(dur)
763771
}
764772

765-
fn expires(&self, instant: Instant) -> bool {
773+
fn expires(&self, instant: Instant, now: Instant) -> bool {
766774
match self.0 {
767775
// Avoid `Instant::elapsed` to avoid issues like rust-lang/rust#86470.
768-
Some(timeout) => Instant::now().saturating_duration_since(instant) > timeout,
776+
Some(timeout) => now.saturating_duration_since(instant) > timeout,
769777
None => false,
770778
}
771779
}
@@ -785,7 +793,7 @@ impl<T: Poolable + 'static, K: Key> IdleTask<T, K> {
785793
async fn run(self) {
786794
use futures_util::future;
787795

788-
let mut sleep = self.timer.sleep_until(Instant::now() + self.duration);
796+
let mut sleep = self.timer.sleep_until(self.timer.now() + self.duration);
789797
let mut on_pool_drop = self.pool_drop_notifier;
790798
loop {
791799
match future::select(&mut on_pool_drop, &mut sleep).await {
@@ -801,7 +809,7 @@ impl<T: Poolable + 'static, K: Key> IdleTask<T, K> {
801809
}
802810
}
803811

804-
let deadline = Instant::now() + self.duration;
812+
let deadline = self.timer.now() + self.duration;
805813
self.timer.reset(&mut sleep, deadline);
806814
}
807815
}
@@ -976,7 +984,16 @@ mod tests {
976984
}
977985

978986
#[tokio::test]
979-
async fn test_pool_timer_removes_expired() {
987+
async fn test_pool_timer_removes_expired_realtime() {
988+
test_pool_timer_removes_expired_inner().await
989+
}
990+
991+
#[tokio::test(start_paused = true)]
992+
async fn test_pool_timer_removes_expired_faketime() {
993+
test_pool_timer_removes_expired_inner().await
994+
}
995+
996+
async fn test_pool_timer_removes_expired_inner() {
980997
let pool = Pool::new(
981998
super::Config {
982999
idle_timeout: Some(Duration::from_millis(10)),

src/common/timer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ impl hyper::rt::Timer for Timer {
3535
fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>> {
3636
self.0.sleep_until(deadline)
3737
}
38+
39+
fn now(&self) -> Instant {
40+
self.0.now()
41+
}
3842
}

src/rt/tokio.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ impl Timer for TokioTimer {
295295
sleep.reset(new_deadline)
296296
}
297297
}
298+
299+
fn now(&self) -> Instant {
300+
tokio::time::Instant::now().into()
301+
}
298302
}
299303

300304
impl TokioTimer {

0 commit comments

Comments
 (0)