2018-03-28 22:26:47 -07:00
|
|
|
use Error;
|
|
|
|
|
use atomic::AtomicU64;
|
2018-08-20 13:01:39 -07:00
|
|
|
use timer::{HandlePriv, Inner};
|
2018-03-28 22:26:47 -07:00
|
|
|
|
|
|
|
|
use futures::Poll;
|
|
|
|
|
use futures::task::AtomicTask;
|
|
|
|
|
|
|
|
|
|
use std::cell::UnsafeCell;
|
|
|
|
|
use std::ptr;
|
|
|
|
|
use std::sync::{Arc, Weak};
|
2018-08-20 21:47:10 -07:00
|
|
|
use std::sync::atomic::AtomicBool;
|
2018-03-28 22:26:47 -07:00
|
|
|
use std::sync::atomic::Ordering::SeqCst;
|
|
|
|
|
use std::time::Instant;
|
|
|
|
|
use std::u64;
|
|
|
|
|
|
2018-03-30 14:21:48 -07:00
|
|
|
/// Internal state shared between a `Delay` instance and the timer.
|
2018-03-28 22:26:47 -07:00
|
|
|
///
|
|
|
|
|
/// This struct is used as a node in two intrusive data structures:
|
|
|
|
|
///
|
|
|
|
|
/// * An atomic stack used to signal to the timer thread that the entry state
|
|
|
|
|
/// has changed. The timer thread will observe the entry on this stack and
|
|
|
|
|
/// perform any actions as necessary.
|
|
|
|
|
///
|
|
|
|
|
/// * A doubly linked list used **only** by the timer thread. Each slot in the
|
|
|
|
|
/// timer wheel is a head pointer to the list of entries that must be
|
|
|
|
|
/// processed during that timer tick.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(crate) struct Entry {
|
|
|
|
|
/// Timer internals. Using a weak pointer allows the timer to shutdown
|
2018-03-30 14:21:48 -07:00
|
|
|
/// without all `Delay` instances having completed.
|
2018-03-28 22:26:47 -07:00
|
|
|
inner: Weak<Inner>,
|
|
|
|
|
|
|
|
|
|
/// Task to notify once the deadline is reached.
|
|
|
|
|
task: AtomicTask,
|
|
|
|
|
|
|
|
|
|
/// Tracks the entry state. This value contains the following information:
|
|
|
|
|
///
|
|
|
|
|
/// * The deadline at which the entry must be "fired".
|
|
|
|
|
/// * A flag indicating if the entry has already been fired.
|
|
|
|
|
/// * Whether or not the entry transitioned to the error state.
|
|
|
|
|
///
|
|
|
|
|
/// When an `Entry` is created, `state` is initialized to the instant at
|
|
|
|
|
/// which the entry must be fired. When a timer is reset to a different
|
|
|
|
|
/// instant, this value is changed.
|
|
|
|
|
state: AtomicU64,
|
|
|
|
|
|
2018-05-08 14:44:17 -04:00
|
|
|
/// When true, the entry is counted by `Inner` towards the max outstanding
|
2018-03-28 22:26:47 -07:00
|
|
|
/// timeouts. The drop fn uses this to know if it should decrement the
|
|
|
|
|
/// counter.
|
|
|
|
|
///
|
|
|
|
|
/// One might think that it would be easier to just not create the `Entry`.
|
2018-03-30 14:21:48 -07:00
|
|
|
/// The problem is that `Delay` expects creating a `Registration` to always
|
2018-03-28 22:26:47 -07:00
|
|
|
/// return a `Registration` instance. This simplifying factor allows it to
|
|
|
|
|
/// improve the struct layout. To do this, we must always allocate the node.
|
|
|
|
|
counted: bool,
|
|
|
|
|
|
2018-05-08 14:44:17 -04:00
|
|
|
/// True when the entry is queued in the "process" stack. This value
|
2018-03-28 22:26:47 -07:00
|
|
|
/// is set before pushing the value and unset after popping the value.
|
2018-08-20 21:47:10 -07:00
|
|
|
pub(super) queued: AtomicBool,
|
2018-03-28 22:26:47 -07:00
|
|
|
|
|
|
|
|
/// Next entry in the "process" linked list.
|
|
|
|
|
///
|
|
|
|
|
/// Represents a strong Arc ref.
|
2018-08-20 21:47:10 -07:00
|
|
|
pub(super) next_atomic: UnsafeCell<*mut Entry>,
|
2018-03-28 22:26:47 -07:00
|
|
|
|
|
|
|
|
/// When the entry expires, relative to the `start` of the timer
|
|
|
|
|
/// (Inner::start). This is only used by the timer.
|
|
|
|
|
///
|
2018-03-30 14:21:48 -07:00
|
|
|
/// A `Delay` instance can be reset to a different deadline by the thread
|
|
|
|
|
/// that owns the `Delay` instance. In this case, the timer thread will not
|
2018-03-28 22:26:47 -07:00
|
|
|
/// immediately know that this has happened. The timer thread must know the
|
|
|
|
|
/// last deadline that it saw as it uses this value to locate the entry in
|
|
|
|
|
/// its wheel.
|
|
|
|
|
///
|
|
|
|
|
/// Once the timer thread observes that the instant has changed, it updates
|
|
|
|
|
/// the wheel and sets this value. The idea is that this value eventually
|
|
|
|
|
/// converges to the value of `state` as the timer thread makes updates.
|
|
|
|
|
when: UnsafeCell<Option<u64>>,
|
|
|
|
|
|
|
|
|
|
/// Next entry in the State's linked list.
|
|
|
|
|
///
|
|
|
|
|
/// This is only accessed by the timer
|
2018-08-20 21:47:10 -07:00
|
|
|
pub(super) next_stack: UnsafeCell<Option<Arc<Entry>>>,
|
2018-03-28 22:26:47 -07:00
|
|
|
|
|
|
|
|
/// Previous entry in the State's linked list.
|
|
|
|
|
///
|
|
|
|
|
/// This is only accessed by the timer and is used to unlink a canceled
|
|
|
|
|
/// entry.
|
|
|
|
|
///
|
|
|
|
|
/// This is a weak reference.
|
2018-08-20 21:47:10 -07:00
|
|
|
pub(super) prev_stack: UnsafeCell<*const Entry>,
|
2018-03-28 22:26:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Flag indicating a timer entry has elapsed
|
|
|
|
|
const ELAPSED: u64 = 1 << 63;
|
|
|
|
|
|
|
|
|
|
/// Flag indicating a timer entry has reached an error state
|
|
|
|
|
const ERROR: u64 = u64::MAX;
|
|
|
|
|
|
|
|
|
|
// ===== impl Entry =====
|
|
|
|
|
|
|
|
|
|
impl Entry {
|
2018-08-20 13:01:39 -07:00
|
|
|
pub fn new(when: u64, handle: HandlePriv) -> Entry {
|
2018-03-28 22:26:47 -07:00
|
|
|
assert!(when > 0 && when < u64::MAX);
|
|
|
|
|
|
|
|
|
|
Entry {
|
|
|
|
|
inner: handle.into_inner(),
|
|
|
|
|
task: AtomicTask::new(),
|
|
|
|
|
state: AtomicU64::new(when),
|
|
|
|
|
counted: true,
|
|
|
|
|
queued: AtomicBool::new(false),
|
|
|
|
|
next_atomic: UnsafeCell::new(ptr::null_mut()),
|
|
|
|
|
when: UnsafeCell::new(None),
|
|
|
|
|
next_stack: UnsafeCell::new(None),
|
|
|
|
|
prev_stack: UnsafeCell::new(ptr::null_mut()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 13:01:39 -07:00
|
|
|
pub fn new_elapsed(handle: HandlePriv) -> Entry {
|
2018-03-28 22:26:47 -07:00
|
|
|
Entry {
|
|
|
|
|
inner: handle.into_inner(),
|
|
|
|
|
task: AtomicTask::new(),
|
|
|
|
|
state: AtomicU64::new(ELAPSED),
|
|
|
|
|
counted: true,
|
|
|
|
|
queued: AtomicBool::new(false),
|
|
|
|
|
next_atomic: UnsafeCell::new(ptr::null_mut()),
|
|
|
|
|
when: UnsafeCell::new(None),
|
|
|
|
|
next_stack: UnsafeCell::new(None),
|
|
|
|
|
prev_stack: UnsafeCell::new(ptr::null_mut()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a new `Entry` that is in the error state. Calling `poll_elapsed` on
|
|
|
|
|
/// this `Entry` will always result in `Err` being returned.
|
|
|
|
|
pub fn new_error() -> Entry {
|
|
|
|
|
Entry {
|
|
|
|
|
inner: Weak::new(),
|
|
|
|
|
task: AtomicTask::new(),
|
|
|
|
|
state: AtomicU64::new(ERROR),
|
|
|
|
|
counted: false,
|
|
|
|
|
queued: AtomicBool::new(false),
|
|
|
|
|
next_atomic: UnsafeCell::new(ptr::null_mut()),
|
|
|
|
|
when: UnsafeCell::new(None),
|
|
|
|
|
next_stack: UnsafeCell::new(None),
|
|
|
|
|
prev_stack: UnsafeCell::new(ptr::null_mut()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The current entry state as known by the timer. This is not the value of
|
|
|
|
|
/// `state`, but lets the timer know how to converge its state to `state`.
|
|
|
|
|
pub fn when_internal(&self) -> Option<u64> {
|
|
|
|
|
unsafe { (*self.when.get()) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_when_internal(&self, when: Option<u64>) {
|
|
|
|
|
unsafe { (*self.when.get()) = when; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Called by `Timer` to load the current value of `state` for processing
|
|
|
|
|
pub fn load_state(&self) -> Option<u64> {
|
|
|
|
|
let state = self.state.load(SeqCst);
|
|
|
|
|
|
|
|
|
|
if is_elapsed(state) {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
Some(state)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_elapsed(&self) -> bool {
|
|
|
|
|
let state = self.state.load(SeqCst);
|
|
|
|
|
is_elapsed(state)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn fire(&self, when: u64) {
|
|
|
|
|
let mut curr = self.state.load(SeqCst);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if is_elapsed(curr) || curr > when {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next = ELAPSED | curr;
|
|
|
|
|
let actual = self.state.compare_and_swap(curr, next, SeqCst);
|
|
|
|
|
|
|
|
|
|
if curr == actual {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curr = actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.task.notify();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn error(&self) {
|
|
|
|
|
// Only transition to the error state if not currently elapsed
|
|
|
|
|
let mut curr = self.state.load(SeqCst);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if is_elapsed(curr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next = ERROR;
|
|
|
|
|
|
|
|
|
|
let actual = self.state.compare_and_swap(curr, next, SeqCst);
|
|
|
|
|
|
|
|
|
|
if curr == actual {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curr = actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.task.notify();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cancel(entry: &Arc<Entry>) {
|
|
|
|
|
let state = entry.state.fetch_or(ELAPSED, SeqCst);
|
|
|
|
|
|
|
|
|
|
if is_elapsed(state) {
|
|
|
|
|
// Nothing more to do
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let inner = match entry.inner.upgrade() {
|
|
|
|
|
Some(inner) => inner,
|
|
|
|
|
None => return,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let _ = inner.queue(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn poll_elapsed(&self) -> Poll<(), Error> {
|
|
|
|
|
use futures::Async::NotReady;
|
|
|
|
|
|
|
|
|
|
let mut curr = self.state.load(SeqCst);
|
|
|
|
|
|
|
|
|
|
if is_elapsed(curr) {
|
|
|
|
|
if curr == ERROR {
|
|
|
|
|
return Err(Error::shutdown());
|
|
|
|
|
} else {
|
|
|
|
|
return Ok(().into());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.task.register();
|
|
|
|
|
|
|
|
|
|
curr = self.state.load(SeqCst).into();
|
|
|
|
|
|
|
|
|
|
if is_elapsed(curr) {
|
|
|
|
|
if curr == ERROR {
|
|
|
|
|
return Err(Error::shutdown());
|
|
|
|
|
} else {
|
|
|
|
|
return Ok(().into());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(NotReady)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn reset(entry: &Arc<Entry>, deadline: Instant) {
|
|
|
|
|
let inner = match entry.inner.upgrade() {
|
|
|
|
|
Some(inner) => inner,
|
|
|
|
|
None => return,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let when = inner.normalize_deadline(deadline);
|
|
|
|
|
let elapsed = inner.elapsed();
|
|
|
|
|
|
|
|
|
|
let mut curr = entry.state.load(SeqCst);
|
|
|
|
|
let mut notify;
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
// In these two cases, there is no work to do when resetting the
|
|
|
|
|
// timer. If the `Entry` is in an error state, then it cannot be
|
|
|
|
|
// used anymore. If resetting the entry to the current value, then
|
|
|
|
|
// the reset is a noop.
|
|
|
|
|
if curr == ERROR || curr == when {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next;
|
|
|
|
|
|
|
|
|
|
if when <= elapsed {
|
|
|
|
|
next = ELAPSED;
|
|
|
|
|
notify = !is_elapsed(curr);
|
|
|
|
|
} else {
|
|
|
|
|
next = when;
|
|
|
|
|
notify = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let actual = entry.state.compare_and_swap(
|
|
|
|
|
curr, next, SeqCst);
|
|
|
|
|
|
|
|
|
|
if curr == actual {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curr = actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if notify {
|
|
|
|
|
let _ = inner.queue(entry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_elapsed(state: u64) -> bool {
|
|
|
|
|
state & ELAPSED == ELAPSED
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Entry {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
if !self.counted {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let inner = match self.inner.upgrade() {
|
|
|
|
|
Some(inner) => inner,
|
|
|
|
|
None => return,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inner.decrement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe impl Send for Entry {}
|
|
|
|
|
unsafe impl Sync for Entry {}
|