diff options
author | bdonlan <bdonlan@gmail.com> | 2020-12-08 16:42:43 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-08 16:42:43 -0800 |
commit | 9706ca92a8deb69d6e29265f21424042fea966c5 (patch) | |
tree | cd77e2148b7cdf03d0fcb38e8e27cf3f7eed1ed9 /tokio/tests | |
parent | fc7a4b3c6e765d6d2b4ea97266cefbf466d52dc9 (diff) |
time: Fix race condition in timer drop (#3229)
Dropping a timer on the millisecond that it was scheduled for, when it was on
the pending list, could result in a panic previously, as we did not record the
pending-list state in cached_when.
Hopefully fixes: ZcashFoundation/zebra#1452
Diffstat (limited to 'tokio/tests')
-rw-r--r-- | tokio/tests/time_sleep.rs | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/tokio/tests/time_sleep.rs b/tokio/tests/time_sleep.rs index d110ec27..20e2b1c6 100644 --- a/tokio/tests/time_sleep.rs +++ b/tokio/tests/time_sleep.rs @@ -308,3 +308,75 @@ async fn no_out_of_bounds_close_to_max() { fn ms(n: u64) -> Duration { Duration::from_millis(n) } + +#[tokio::test] +async fn drop_after_reschedule_at_new_scheduled_time() { + use futures::poll; + + tokio::time::pause(); + + let start = tokio::time::Instant::now(); + + let mut a = tokio::time::sleep(Duration::from_millis(5)); + let mut b = tokio::time::sleep(Duration::from_millis(5)); + let mut c = tokio::time::sleep(Duration::from_millis(10)); + + let _ = poll!(&mut a); + let _ = poll!(&mut b); + let _ = poll!(&mut c); + + b.reset(start + Duration::from_millis(10)); + a.await; + + drop(b); +} + +#[tokio::test] +async fn drop_from_wake() { + use std::future::Future; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::{Arc, Mutex}; + use std::task::Context; + + let panicked = Arc::new(AtomicBool::new(false)); + let list: Arc<Mutex<Vec<tokio::time::Sleep>>> = Arc::new(Mutex::new(Vec::new())); + + let arc_wake = Arc::new(DropWaker(panicked.clone(), list.clone())); + let arc_wake = futures::task::waker(arc_wake); + + tokio::time::pause(); + + let mut lock = list.lock().unwrap(); + + for _ in 0..100 { + let mut timer = tokio::time::sleep(Duration::from_millis(10)); + + let _ = std::pin::Pin::new(&mut timer).poll(&mut Context::from_waker(&arc_wake)); + + lock.push(timer); + } + + drop(lock); + + tokio::time::sleep(Duration::from_millis(11)).await; + + assert!( + !panicked.load(Ordering::SeqCst), + "paniced when dropping timers" + ); + + #[derive(Clone)] + struct DropWaker(Arc<AtomicBool>, Arc<Mutex<Vec<tokio::time::Sleep>>>); + + impl futures::task::ArcWake for DropWaker { + fn wake_by_ref(arc_self: &Arc<Self>) { + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + *arc_self.1.lock().expect("panic in lock") = Vec::new() + })); + + if result.is_err() { + arc_self.0.store(true, Ordering::SeqCst); + } + } + } +} |