use futures::StreamExt; use tokio::time::{self, sleep, sleep_until, Duration, Instant}; use tokio_test::{assert_pending, assert_ready, task}; use tokio_util::time::DelayQueue;
letmut queue = task::spawn(DelayQueue::new()); let now = Instant::now();
sleep(ms(1)).await;
let key = queue.insert_at("foo", now + ms(200));
assert_pending!(poll!(queue));
sleep(ms(3)).await;
queue.reset_at(&key, now + ms(50));
sleep(ms(20)).await;
queue.reset_at(&key, now + ms(40));
sleep(ms(20)).await;
assert!(queue.is_woken());
}
/// Regression test: Given an entry inserted with a deadline in the past, so /// that it is placed directly on the expired queue, reset the entry to a /// deadline in the future. Validate that this leaves the entry and queue in an /// internally consistent state by running an additional reset on the entry /// before polling it to completion. #[tokio::test] asyncfn repeatedly_reset_entry_inserted_as_expired() {
time::pause();
// Instants before the start of the test seem to break in wasm.
time::sleep(ms(1000)).await;
letmut queue = task::spawn(DelayQueue::new()); let now = Instant::now();
let key = queue.insert_at("foo", now - ms(100));
queue.reset_at(&key, now + ms(100));
queue.reset_at(&key, now + ms(50));
assert_pending!(poll!(queue));
time::sleep_until(now + ms(60)).await;
assert!(queue.is_woken());
let entry = assert_ready_some!(poll!(queue)).into_inner();
assert_eq!(entry, "foo");
let entry = assert_ready!(poll!(queue));
assert!(entry.is_none());
}
let entry = queue.remove(&key);
assert_eq!(entry.into_inner(), "foo");
}
/// Regression test: it should be possible to remove entries which fall in the /// 0th slot of the internal timer wheel — that is, entries whose expiration /// (a) falls at the beginning of one of the wheel's hierarchical levels and (b) /// is equal to the wheel's current elapsed time. #[tokio::test] asyncfn remove_at_timer_wheel_threshold() {
time::pause();
letmut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
let key1 = queue.insert_at("foo", now + ms(64)); let key2 = queue.insert_at("bar", now + ms(64));
sleep(ms(80)).await;
let entry = assert_ready_some!(poll!(queue)).into_inner();
match entry { "foo" => { let entry = queue.remove(&key2).into_inner();
assert_eq!(entry, "bar");
} "bar" => { let entry = queue.remove(&key1).into_inner();
assert_eq!(entry, "foo");
}
other => panic!("other: {other:?}"),
}
}
// At this point the queue hasn't been polled, so `elapsed` on the wheel // for the queue is still at 0 and hence the 1ms resolution slots cover // [0-64). Resetting the time on the entry to 120 causes it to get put in // the [64-128) slot. As the queue knows that the first entry is within // that slot, but doesn't know when, it must wake immediately to advance // the wheel.
queue.reset_at(&foo, now + ms(120));
assert!(queue.is_woken());
// At this point the queue hasn't been polled, so `elapsed` on the wheel // for the queue is still at 0 and hence the 1ms resolution slots cover // [0-64). Resetting the time on the entry to 120 causes it to get put in // the [64-128) slot. As the queue knows that the first entry is within // that slot, but doesn't know when, it must wake immediately to advance // the wheel.
queue.reset_at(&foo, now + ms(120));
assert!(queue.is_woken());
#[tokio::test(start_paused = true)] // Trigger a re-mapping of keys in the slab due to a `compact` call and // test removal of re-mapped keys asyncfn compact_remove_remapped_keys() { letmut queue = task::spawn(DelayQueue::new());
let now = Instant::now();
queue.insert_at("foo1", now + ms(10));
queue.insert_at("foo2", now + ms(10));
// should be assigned indices 3 and 4 let key3 = queue.insert_at("foo3", now + ms(20)); let key4 = queue.insert_at("foo4", now + ms(20));
sleep(ms(10)).await;
letmut res = vec![]; while res.len() < 2 { let entry = assert_ready_some!(poll!(queue));
res.push(entry.into_inner());
}
// items corresponding to `foo3` and `foo4` will be assigned // new indices here
queue.compact();
queue.insert_at("foo5", now + ms(10));
// test removal of re-mapped keys let expired3 = queue.remove(&key3); let expired4 = queue.remove(&key4);
queue.insert_at("foo1", now + ms(10));
queue.insert_at("foo2", now + ms(10));
// should be assigned indices 3 and 4
queue.insert_at("foo3", now + ms(20)); let key4 = queue.insert_at("foo4", now + ms(20));
sleep(ms(10)).await;
letmut res = vec![]; while res.len() < 2 { let entry = assert_ready_some!(poll!(queue));
res.push(entry.into_inner());
}
// items corresponding to `foo3` and `foo4` should be assigned // new indices
queue.compact();
now = Instant::now();
queue.insert_at("foo5", now + ms(10)); let key6 = queue.insert_at("foo6", now + ms(10));
queue.reset_at(&key4, now + ms(20));
queue.reset_at(&key6, now + ms(20));
// foo3 and foo5 will expire
sleep(ms(10)).await;
while res.len() < 4 { let entry = assert_ready_some!(poll!(queue));
res.push(entry.into_inner());
}
sleep(ms(10)).await;
while res.len() < 6 { let entry = assert_ready_some!(poll!(queue));
res.push(entry.into_inner());
}
let entry = assert_ready!(poll!(queue));
assert!(entry.is_none());
}
#[tokio::test(start_paused = true)] asyncfn item_expiry_greater_than_wheel() { // This function tests that a delay queue that has existed for at least 2^36 milliseconds won't panic when a new item is inserted. letmut queue = DelayQueue::new(); for _ in0..2 {
tokio::time::advance(Duration::from_millis(1 << 35)).await;
queue.insert(0, Duration::from_millis(0));
queue.next().await;
} // This should not panic let no_panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
queue.insert(1, Duration::from_millis(1));
}));
assert!(no_panic.is_ok());
}
#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")] #[tokio::test(start_paused = true)] #[cfg(panic = "unwind")] asyncfn remove_after_compact() { let now = Instant::now(); letmut queue = DelayQueue::new();
let foo_key = queue.insert_at("foo", now + ms(10));
queue.insert_at("bar", now + ms(20));
queue.remove(&foo_key);
queue.compact();
let panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
queue.remove(&foo_key);
}));
assert!(panic.is_err());
}
#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")] #[tokio::test(start_paused = true)] #[cfg(panic = "unwind")] asyncfn remove_after_compact_poll() { let now = Instant::now(); letmut queue = task::spawn(DelayQueue::new());
let foo_key = queue.insert_at("foo", now + ms(10));
queue.insert_at("bar", now + ms(20));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.