diff --git a/tokio/src/time/driver/atomic_stack.rs b/tokio/src/time/driver/atomic_stack.rs index 036d283dfd3..23f54b9fe4c 100644 --- a/tokio/src/time/driver/atomic_stack.rs +++ b/tokio/src/time/driver/atomic_stack.rs @@ -118,7 +118,7 @@ impl Drop for AtomicStackEntries { fn drop(&mut self) { for entry in self { // Flag the entry as errored - entry.error(); + entry.error(Error::shutdown()); } } } diff --git a/tokio/src/time/driver/entry.rs b/tokio/src/time/driver/entry.rs index 60c3967d86a..95f7cff0657 100644 --- a/tokio/src/time/driver/entry.rs +++ b/tokio/src/time/driver/entry.rs @@ -2,12 +2,11 @@ use crate::loom::sync::atomic::AtomicU64; use crate::sync::AtomicWaker; use crate::time::driver::{Handle, Inner}; use crate::time::{Duration, Error, Instant}; -use crate::time::wheel::MAX_DURATION; use std::cell::UnsafeCell; use std::ptr; -use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering::SeqCst; +use std::sync::atomic::{AtomicBool, AtomicU8}; use std::sync::{Arc, Weak}; use std::task::{self, Poll}; use std::u64; @@ -45,6 +44,11 @@ pub(crate) struct Entry { /// instant, this value is changed. state: AtomicU64, + /// Stores the actual error. If `state` indicates that an error occurred, + /// this is guaranteed to be a non-zero value representing the first error + /// that occurred. Otherwise its value is undefined. + error: AtomicU8, + /// Task to notify once the deadline is reached. waker: AtomicWaker, @@ -110,14 +114,13 @@ impl Entry { let entry: Entry; // Increment the number of active timeouts - if inner.increment().is_err() { - entry = Entry::new2(deadline, duration, Weak::new(), ERROR) + if let Err(err) = inner.increment() { + entry = Entry::new2(deadline, duration, Weak::new(), ERROR); + entry.error(err); } else { let when = inner.normalize_deadline(deadline); let state = if when <= inner.elapsed() { ELAPSED - } else if when - inner.elapsed() > MAX_DURATION { - panic!("timer duration exceeds maximum duration"); } else { when }; @@ -125,8 +128,8 @@ impl Entry { } let entry = Arc::new(entry); - if inner.queue(&entry).is_err() { - entry.error(); + if let Err(err) = inner.queue(&entry) { + entry.error(err); } entry @@ -192,7 +195,12 @@ impl Entry { self.waker.wake(); } - pub(crate) fn error(&self) { + pub(crate) fn error(&self, error: Error) { + // Record the precise nature of the error, if there isn't already an + // error present. If we don't actually transition to the error state + // below, that's fine, as the error details we set here will be ignored. + self.error.compare_and_swap(0, error.as_u8(), SeqCst); + // Only transition to the error state if not currently elapsed let mut curr = self.state.load(SeqCst); @@ -237,7 +245,7 @@ impl Entry { if is_elapsed(curr) { return Poll::Ready(if curr == ERROR { - Err(Error::shutdown()) + Err(Error::from_u8(self.error.load(SeqCst))) } else { Ok(()) }); @@ -249,7 +257,7 @@ impl Entry { if is_elapsed(curr) { return Poll::Ready(if curr == ERROR { - Err(Error::shutdown()) + Err(Error::from_u8(self.error.load(SeqCst))) } else { Ok(()) }); @@ -312,6 +320,7 @@ impl Entry { waker: AtomicWaker::new(), state: AtomicU64::new(state), queued: AtomicBool::new(false), + error: AtomicU8::new(0), next_atomic: UnsafeCell::new(ptr::null_mut()), when: UnsafeCell::new(None), next_stack: UnsafeCell::new(None), diff --git a/tokio/src/time/driver/mod.rs b/tokio/src/time/driver/mod.rs index 5d48742324c..6aa4c8c6743 100644 --- a/tokio/src/time/driver/mod.rs +++ b/tokio/src/time/driver/mod.rs @@ -219,7 +219,7 @@ where // The entry's deadline is invalid, so error it and update the // internal state accordingly. entry.set_when_internal(None); - entry.error(); + entry.error(Error::invalid()); } } } @@ -297,7 +297,7 @@ impl Drop for Driver { let mut poll = wheel::Poll::new(u64::MAX); while let Some(entry) = self.wheel.poll(&mut poll, &mut ()) { - entry.error(); + entry.error(Error::shutdown()); } } } diff --git a/tokio/src/time/error.rs b/tokio/src/time/error.rs index 994eec1f483..96ba433becb 100644 --- a/tokio/src/time/error.rs +++ b/tokio/src/time/error.rs @@ -24,10 +24,12 @@ use std::fmt; #[derive(Debug)] pub struct Error(Kind); -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] +#[repr(u8)] enum Kind { - Shutdown, - AtCapacity, + Shutdown = 1, + AtCapacity = 2, + Invalid = 3, } impl Error { @@ -56,6 +58,32 @@ impl Error { _ => false, } } + + /// Create an error representing a misconfigured timer. + pub fn invalid() -> Error { + Error(Invalid) + } + + /// Returns `true` if the error was caused by the timer being misconfigured. + pub fn is_invalid(&self) -> bool { + match self.0 { + Kind::Invalid => true, + _ => false, + } + } + + pub(crate) fn as_u8(&self) -> u8 { + self.0 as u8 + } + + pub(crate) fn from_u8(n: u8) -> Self { + Error(match n { + 1 => Shutdown, + 2 => AtCapacity, + 3 => Invalid, + _ => panic!("u8 does not correspond to any time error variant"), + }) + } } impl error::Error for Error {} @@ -66,6 +94,7 @@ impl fmt::Display for Error { let descr = match self.0 { Shutdown => "timer is shutdown", AtCapacity => "timer is at capacity and cannot create a new entry", + Invalid => "timer duration exceeds maximum duration", }; write!(fmt, "{}", descr) } diff --git a/tokio/src/time/wheel/mod.rs b/tokio/src/time/wheel/mod.rs index 9f3f635bf62..c5f0f458a79 100644 --- a/tokio/src/time/wheel/mod.rs +++ b/tokio/src/time/wheel/mod.rs @@ -43,7 +43,7 @@ pub(crate) struct Wheel { const NUM_LEVELS: usize = 6; /// The maximum duration of a delay -pub(crate) const MAX_DURATION: u64 = 1 << (6 * NUM_LEVELS); +const MAX_DURATION: u64 = 1 << (6 * NUM_LEVELS); #[derive(Debug)] pub(crate) enum InsertError {