Provide a timer implementation (#249)

This patch adds a new crate: tokio-timer. This crate provides an
efficient timer implemeentation designed for use in Tokio based
applications.

The timer users a hierarchical hashed timer wheel algorithm with six
levels, each having 64 slots. This allows the timer to have a resolution
of 1ms while maintaining O(1) complexity for insert, removal, and firing
of timeouts.

There already exists a tokio-timer crate. This is a complete rewrite
which solves the outstanding problems with the existing tokio-timer
library.

Closes #146.
This commit is contained in:
Carl Lerche
2018-03-28 22:26:47 -07:00
committed by GitHub
parent ad189826f4
commit 19500f7df8
24 changed files with 3398 additions and 0 deletions
+559
View File
@@ -0,0 +1,559 @@
use Error;
use atomic::AtomicU64;
use timer::{Handle, Inner};
use futures::Poll;
use futures::task::AtomicTask;
use std::cell::UnsafeCell;
use std::ptr;
use std::sync::{Arc, Weak};
use std::sync::atomic::{AtomicBool, AtomicPtr};
use std::sync::atomic::Ordering::SeqCst;
use std::time::Instant;
use std::u64;
/// Internal state shared between a `Sleep` instance and the timer.
///
/// 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
/// without all `Sleep` instances having completed.
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,
/// When true, the entry is counted by `Inner` towards the max oustanding
/// 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`.
/// The problem is that `Sleep` expects creating a `Registration` to always
/// 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,
/// True wheen the entry is queued in the "process" stack. This value
/// is set before pushing the value and unset after popping the value.
queued: AtomicBool,
/// Next entry in the "process" linked list.
///
/// Represents a strong Arc ref.
next_atomic: UnsafeCell<*mut Entry>,
/// When the entry expires, relative to the `start` of the timer
/// (Inner::start). This is only used by the timer.
///
/// A `Sleep` instance can be reset to a different deadline by the thread
/// that owns the `Sleep` instance. In this case, the timer thread will not
/// 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
next_stack: UnsafeCell<Option<Arc<Entry>>>,
/// 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.
prev_stack: UnsafeCell<*const Entry>,
}
/// A doubly linked stack
pub(crate) struct Stack {
head: Option<Arc<Entry>>,
}
/// A stack of `Entry` nodes
#[derive(Debug)]
pub(crate) struct AtomicStack {
/// Stack head
head: AtomicPtr<Entry>,
}
/// Entries that were removed from the stack
#[derive(Debug)]
pub(crate) struct AtomicStackEntries {
ptr: *mut Entry,
}
/// 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;
/// Used to indicate that the timer has shutdown.
const SHUTDOWN: *mut Entry = 1 as *mut _;
// ===== impl Entry =====
impl Entry {
pub fn new(when: u64, handle: Handle) -> Entry {
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()),
}
}
pub fn new_elapsed(handle: Handle) -> Entry {
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 {}
// ===== impl Stack =====
impl Stack {
pub fn new() -> Stack {
Stack { head: None }
}
pub fn is_empty(&self) -> bool {
self.head.is_none()
}
/// Push an entry to the head of the linked list
pub fn push(&mut self, entry: Arc<Entry>) {
// Get a pointer to the entry to for the prev link
let ptr: *const Entry = &*entry as *const _;
// Remove the old head entry
let old = self.head.take();
unsafe {
// Ensure the entry is not already in a stack.
debug_assert!((*entry.next_stack.get()).is_none());
debug_assert!((*entry.prev_stack.get()).is_null());
if let Some(ref entry) = old.as_ref() {
debug_assert!({
// The head is not already set to the entry
ptr != &***entry as *const _
});
// Set the previous link on the old head
*entry.prev_stack.get() = ptr;
}
// Set this entry's next pointer
*entry.next_stack.get() = old;
}
// Update the head pointer
self.head = Some(entry);
}
/// Pop the head of the linked list
pub fn pop(&mut self) -> Option<Arc<Entry>> {
let entry = self.head.take();
unsafe {
if let Some(entry) = entry.as_ref() {
self.head = (*entry.next_stack.get()).take();
if let Some(entry) = self.head.as_ref() {
*entry.prev_stack.get() = ptr::null();
}
*entry.prev_stack.get() = ptr::null();
}
}
entry
}
/// Remove the entry from the linked list
///
/// The caller must ensure that the entry actually is contained by the list.
pub fn remove(&mut self, entry: &Entry) {
unsafe {
// Ensure that the entry is in fact contained by the stack
debug_assert!({
// This walks the full linked list even if an entry is found.
let mut next = self.head.as_ref();
let mut contains = false;
while let Some(n) = next {
if entry as *const _ == &**n as *const _ {
debug_assert!(!contains);
contains = true;
}
next = (*n.next_stack.get()).as_ref();
}
contains
});
// Unlink `entry` from the next node
let next = (*entry.next_stack.get()).take();
if let Some(next) = next.as_ref() {
(*next.prev_stack.get()) = *entry.prev_stack.get();
}
// Unlink `entry` from the prev node
if let Some(prev) = (*entry.prev_stack.get()).as_ref() {
*prev.next_stack.get() = next;
} else {
// It is the head
self.head = next;
}
// Unset the prev pointer
*entry.prev_stack.get() = ptr::null();
}
}
}
// ===== impl AtomicStack =====
impl AtomicStack {
pub fn new() -> AtomicStack {
AtomicStack { head: AtomicPtr::new(ptr::null_mut()) }
}
/// Push an entry onto the stack.
///
/// Returns `true` if the entry was pushed, `false` if the entry is already
/// on the stack, `Err` if the timer is shutdown.
pub fn push(&self, entry: &Arc<Entry>) -> Result<bool, Error> {
// First, set the queued bit on the entry
let queued = entry.queued.fetch_or(true, SeqCst).into();
if queued {
// Already queued, nothing more to do
return Ok(false);
}
let ptr = Arc::into_raw(entry.clone()) as *mut _;
let mut curr = self.head.load(SeqCst);
loop {
if curr == SHUTDOWN {
// Don't leak the entry node
let _ = unsafe { Arc::from_raw(ptr) };
return Err(Error::shutdown());
}
// Update the `next` pointer. This is safe because setting the queued
// bit is a "lock" on this field.
unsafe {
*(entry.next_atomic.get()) = curr;
}
let actual = self.head.compare_and_swap(curr, ptr, SeqCst);
if actual == curr {
break;
}
curr = actual;
}
Ok(true)
}
/// Take all entries from the stack
pub fn take(&self) -> AtomicStackEntries {
let ptr = self.head.swap(ptr::null_mut(), SeqCst);
AtomicStackEntries { ptr }
}
/// Drain all remaining nodes in the stack and prevent any new nodes from
/// being pushed onto the stack.
pub fn shutdown(&self) {
// Shutdown the processing queue
let ptr = self.head.swap(SHUTDOWN, SeqCst);
// Let the drop fn of `AtomicStackEntries` handle draining the stack
drop(AtomicStackEntries { ptr });
}
}
// ===== impl AtomicStackEntries =====
impl Iterator for AtomicStackEntries {
type Item = Arc<Entry>;
fn next(&mut self) -> Option<Self::Item> {
if self.ptr.is_null() {
return None;
}
// Convert the pointer to an `Arc<Entry>`
let entry = unsafe { Arc::from_raw(self.ptr) };
// Update `self.ptr` to point to the next element of the stack
self.ptr = unsafe { (*entry.next_atomic.get()) };
// Unset the queued flag
let res = entry.queued.fetch_and(false, SeqCst);
debug_assert!(res);
// Return the entry
Some(entry)
}
}
impl Drop for AtomicStackEntries {
fn drop(&mut self) {
while let Some(entry) = self.next() {
// Flag the entry as errored
entry.error();
}
}
}
+127
View File
@@ -0,0 +1,127 @@
use {Error, Sleep, Deadline, Interval};
use timer::{Registration, Inner};
use tokio_executor::Enter;
use std::cell::RefCell;
use std::sync::{Arc, Weak};
use std::time::{Duration, Instant};
/// Handle to timer instance.
///
/// The `Handle` allows creating `Sleep` instances that are driven by the
/// associated timer.
///
/// A `Handle` is obtained by calling [`Timer::handle`].
///
/// [`Timer::handle`]: struct.Timer.html#method.handle
#[derive(Debug, Clone)]
pub struct Handle {
inner: Weak<Inner>,
}
/// Tracks the timer for the current execution context.
thread_local!(static CURRENT_TIMER: RefCell<Option<Handle>> = RefCell::new(None));
/// Set the default timer for the duration of the closure.
///
/// From within the closure, [`Sleep`] instances that are created via
/// [`Sleep::new`] can be used.
///
/// # Panics
///
/// This function panics if there already is a default timer set.
///
/// [`Sleep`]: ../struct.Sleep.html
pub fn with_default<F, R>(handle: &Handle, enter: &mut Enter, f: F) -> R
where F: FnOnce(&mut Enter) -> R
{
// Ensure that the timer is removed from the thread-local context
// when leaving the scope. This handles cases that involve panicking.
struct Reset;
impl Drop for Reset {
fn drop(&mut self) {
CURRENT_TIMER.with(|current| {
let mut current = current.borrow_mut();
*current = None;
});
}
}
// This ensures the value for the current timer gets reset even if there is
// a panic.
let _r = Reset;
CURRENT_TIMER.with(|current| {
{
let mut current = current.borrow_mut();
assert!(current.is_none(), "default Tokio timer already set \
for execution context");
*current = Some(handle.clone());
}
f(enter)
})
}
impl Handle {
pub(crate) fn new(inner: Weak<Inner>) -> Handle {
Handle { inner }
}
/// Returns a handle to the current timer.
///
/// The current timer is the timer that is currently set as default using
/// [`with_default`].
///
/// This function should only be called from within the context of
/// [`with_default`]. Calling this function from outside of this context
/// will return a `Handle` that does not reference a timer. `Sleep`
/// instances created with this handle will error.
///
/// [`with_default`]: ../fn.with_default.html
pub fn current() -> Handle {
Handle::try_current()
.unwrap_or(Handle { inner: Weak::new() })
}
/// Create a `Sleep` driven by this handle's associated `Timer`.
pub fn sleep(&self, deadline: Instant) -> Sleep {
let registration = Registration::new_with_handle(deadline, self.clone());
Sleep::new_with_registration(deadline, registration)
}
/// Create a `Deadline` driven by this handle's associated `Timer`.
pub fn deadline<T>(&self, future: T, deadline: Instant) -> Deadline<T> {
Deadline::new_with_sleep(future, self.sleep(deadline))
}
/// Create a new `Interval` that starts at `at` and yields every `duration`
/// interval after that.
pub fn interval(&self, at: Instant, duration: Duration) -> Interval {
Interval::new_with_sleep(self.sleep(at), duration)
}
/// Try to get a handle to the current timer.
///
/// Returns `Err` if no handle is found.
pub(crate) fn try_current() -> Result<Handle, Error> {
CURRENT_TIMER.with(|current| {
match *current.borrow() {
Some(ref handle) => Ok(handle.clone()),
None => Err(Error::shutdown()),
}
})
}
/// Try to return a strong ref to the inner
pub(crate) fn inner(&self) -> Option<Arc<Inner>> {
self.inner.upgrade()
}
/// Consume the handle, returning the weak Inner ref.
pub(crate) fn into_inner(self) -> Weak<Inner> {
self.inner
}
}
+201
View File
@@ -0,0 +1,201 @@
use timer::{entry, Entry};
use std::fmt;
use std::sync::Arc;
/// Wheel for a single level in the timer. This wheel contains 64 slots.
pub(crate) struct Level {
level: usize,
/// Bit field tracking which slots currently contain entries.
///
/// Using a bit field to track slots that contain entries allows avoiding a
/// scan to find entries. This field is updated when entries are added or
/// removed from a slot.
///
/// The least-significant bit represents slot zero.
occupied: u64,
/// Slots
slot: [entry::Stack; LEVEL_MULT],
}
/// Indicates when a slot must be processed next.
#[derive(Debug)]
pub struct Expiration {
/// The level containing the slot.
pub level: usize,
/// The slot index.
pub slot: usize,
/// The instant at which the slot needs to be processed.
pub deadline: u64,
}
/// Level multiplier.
///
/// Being a power of 2 is very important.
const LEVEL_MULT: usize = 64;
impl Level {
pub fn new(level: usize) -> Level {
// Rust's derived implementations for arrays require that the value
// contained by the array be `Copy`. So, here we have to manually
// initialize every single slot.
macro_rules! s {
() => { entry::Stack::new() };
};
Level {
level,
occupied: 0,
slot: [
// It does not look like the necessary traits are
// derived for [T; 64].
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
s!(), s!(), s!(), s!(), s!(), s!(), s!(), s!(),
],
}
}
/// Finds the slot that needs to be processed next and returns the slot and
/// `Instant` at which this slot must be processed.
pub fn next_expiration(&self, now: u64) -> Option<Expiration> {
// Use the `occupied` bit field to get the index of the next slot that
// needs to be processed.
let slot = match self.next_occupied_slot(now) {
Some(slot) => slot,
None => return None,
};
// From the slot index, calculate the `Instant` at which it needs to be
// processed. This value *must* be in the future with respect to `now`.
let level_range = level_range(self.level);
let slot_range = slot_range(self.level);
// TODO: This can probably be simplified w/ power of 2 math
let level_start = now - (now % level_range);
let deadline = level_start + slot as u64 * slot_range;
debug_assert!(deadline >= now, "deadline={}; now={}; level={}; slot={}; occupied={:b}",
deadline, now, self.level, slot, self.occupied);
Some(Expiration {
level: self.level,
slot,
deadline,
})
}
fn next_occupied_slot(&self, now: u64) -> Option<usize> {
if self.occupied == 0 {
return None;
}
// Get the slot for now using Maths
let now_slot = (now / slot_range(self.level)) as usize;
let occupied = self.occupied.rotate_right(now_slot as u32);
let zeros = occupied.trailing_zeros() as usize;
let slot = (zeros + now_slot) % 64;
Some(slot)
}
pub fn add_entry(&mut self, entry: Arc<Entry>, when: u64) {
let slot = slot_for(when, self.level);
self.slot[slot].push(entry);
self.occupied |= occupied_bit(slot);
}
pub fn remove_entry(&mut self, entry: &Entry, when: u64) {
let slot = slot_for(when, self.level);
self.slot[slot].remove(entry);
if self.slot[slot].is_empty() {
// The bit is currently set
debug_assert!(self.occupied & occupied_bit(slot) != 0);
// Unset the bit
self.occupied ^= occupied_bit(slot);
}
}
pub fn pop_entry_slot(&mut self, slot: usize) -> Option<Arc<Entry>> {
let ret = self.slot[slot].pop();
if ret.is_some() && self.slot[slot].is_empty() {
// The bit is currently set
debug_assert!(self.occupied & occupied_bit(slot) != 0);
self.occupied ^= occupied_bit(slot);
}
ret
}
}
impl Drop for Level {
fn drop(&mut self) {
while let Some(slot) = self.next_occupied_slot(0) {
// This should always have one
let entry = self.pop_entry_slot(slot)
.expect("occupied bit set invalid");
entry.error();
}
}
}
impl fmt::Debug for Level {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Level")
.field("occupied", &self.occupied)
.finish()
}
}
fn occupied_bit(slot: usize) -> u64 {
(1 << slot)
}
fn slot_range(level: usize) -> u64 {
LEVEL_MULT.pow(level as u32) as u64
}
fn level_range(level: usize) -> u64 {
LEVEL_MULT as u64 * slot_range(level)
}
/// Convert a duration (milliseconds) and a level to a slot position
fn slot_for(duration: u64, level: usize) -> usize {
((duration >> (level * 6)) % LEVEL_MULT as u64) as usize
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_slot_for() {
for pos in 1..64 {
assert_eq!(pos as usize, slot_for(pos, 0));
}
for level in 1..5 {
for pos in level..64 {
let a = pos * 64_usize.pow(level as u32);
assert_eq!(pos as usize, slot_for(a as u64, level));
}
}
}
}
+645
View File
@@ -0,0 +1,645 @@
//! Timer implementation.
//!
//! This module contains the types needed to run a timer.
//!
//! The [`Timer`] type runs the timer logic. It holds all the necessary state
//! to track all associated [`Sleep`] instances and delivering notifications
//! once the deadlines are reached.
//!
//! The [`Handle`] type is a reference to a [`Timer`] instance. This type is
//! `Clone`, `Send`, and `Sync`. This type is used to create instances of
//! [`Sleep`].
//!
//! The [`Now`] trait describes how to get an `Instance` representing the
//! current moment in time. [`SystemNow`] is the default implementation, where
//! [`Now::now`] is implemented by calling `Instant::now`.
//!
//! [`Timer`] is generic over [`Now`]. This allows the source of time to be
//! customized. This ability is especially useful in tests and any environment
//! where determinism is necessary.
//!
//! Note, when using the Tokio runtime, the `Timer` does not need to be manually
//! setup as the runtime comes pre-configured with a `Timer` instance.
//!
//! [`Timer`]: struct.Timer.html
//! [`Handle`]: struct.Handle.html
//! [`Sleep`]: ../struct.Sleep.html
//! [`Now`]: trait.Now.html
//! [`Now::now`]: trait.Now.html#method.now
mod entry;
mod handle;
mod level;
mod now;
mod registration;
use self::entry::Entry;
use self::level::{Level, Expiration};
pub use self::handle::{Handle, with_default};
pub use self::now::{Now, SystemNow};
pub(crate) use self::registration::Registration;
use Error;
use atomic::AtomicU64;
use tokio_executor::park::{Park, Unpark, ParkThread};
use std::{cmp, fmt};
use std::time::{Duration, Instant};
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::usize;
/// Timer implementation that drives [`Sleep`], [`Interval`], and [`Deadline`].
///
/// A `Timer` instance tracks the state necessary for managing time and
/// notifying the [`Sleep`] instances once their deadlines are reached.
///
/// It is expected that a single `Timer` instance manages many individual
/// `Sleep` instances. The `Timer` implementation is thread-safe and, as such,
/// is able to handle callers from across threads.
///
/// Callers do not use `Timer` directly to create `Sleep` instances. Instead,
/// [`Handle`] is used. A handle for the timer instance is obtained by calling
/// [`handle`]. [`Handle`] is the type that implements `Clone` and is `Send +
/// Sync`.
///
/// After creating the `Timer` instance, the caller must repeatedly call
/// [`turn`]. The timer will perform no work unless [`turn`] is called
/// repeatedly.
///
/// The `Timer` has a resolution of one millisecond. Any unit of time that falls
/// between milliseconds are rounded up to the next millisecond.
///
/// When the `Timer` instance is dropped, any outstanding `Sleep` instance that
/// has not elapsed will be notified with an error. At this point, calling
/// `poll` on the sleep instance will result in `Err` being returned.
///
/// # Implementation
///
/// `Timer` is based on the [paper by Varghese and Lauck][paper].
///
/// A hashed timing wheel is a vector of slots, where each slot handles a time
/// slice. As time progresses, the timer walks over the slot for the current
/// instant, and processes each entry for that slot. When the timer reaches the
/// end of the wheel, it starts again at the beginning.
///
/// The `Timer` implementation maintains six wheels arranged in a set of levels.
/// As the levels go up, the slots of the associated wheel represent larger
/// intervals of time. At each level, the wheel has 64 slots. Each slot covers a
/// range of time equal to the wheel at the lower level. At level zero, each
/// slot represents one millisecond of time.
///
/// The wheels are:
///
/// * Level 0: 64 x 1 millisecond slots.
/// * Level 1: 64 x 64 millisecond slots.
/// * Level 2: 64 x ~4 second slots.
/// * Level 3: 64 x ~4 minute slots.
/// * Level 4: 64 x ~4 hour slots.
/// * Level 5: 64 x ~12 day slots.
///
/// When the timer processes entries at level zero, it will notify all the
/// [`Sleep`] instances as their deadlines have been reached. For all higher
/// levels, all entries will be redistributed across the wheel at the next level
/// down. Eventually, as time progresses, entries will `Sleep` instances will
/// either be canceled (dropped) or their associated entries will reach level
/// zero and be notified.
///
/// [`Sleep`]: ../struct.Sleep.html
/// [`Interval`]: ../struct.Interval.html
/// [`Deadline`]: ../struct.Deadline.html
/// [paper]: http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf
/// [`handle`]: #method.handle
/// [`turn`]: #method.turn
/// [`Handle`]: struct.Handle.html
#[derive(Debug)]
pub struct Timer<T, N = SystemNow> {
/// Shared state
inner: Arc<Inner>,
/// The number of milliseconds elapsed since the timer started.
elapsed: u64,
/// Timer wheel.
///
/// Levels:
///
/// * 1 ms slots / 64 ms range
/// * 64 ms slots / ~ 4 sec range
/// * ~ 4 sec slots / ~ 4 min range
/// * ~ 4 min slots / ~ 4 hr range
/// * ~ 4 hr slots / ~ 12 day range
/// * ~ 12 day slots / ~ 2 yr range
levels: Vec<Level>,
/// Thread parker. The `Timer` park implementation delegates to this.
park: T,
/// Source of "now" instances
now: N,
}
/// Return value from the `turn` method on `Timer`.
///
/// Currently this value doesn't actually provide any functionality, but it may
/// in the future give insight into what happened during `turn`.
#[derive(Debug)]
pub struct Turn(());
/// Timer state shared between `Timer`, `Handle`, and `Registration`.
pub(crate) struct Inner {
/// The instant at which the timer started running.
start: Instant,
/// The last published timer `elapsed` value.
elapsed: AtomicU64,
/// Number of active timeouts
num: AtomicUsize,
/// Head of the "process" linked list.
process: entry::AtomicStack,
/// Unparks the timer thread.
unpark: Box<Unpark>,
}
/// Number of levels. Each level has 64 slots. By using 6 levels with 64 slots
/// each, the timer is able to track time up to 2 years into the future with a
/// precision of 1 millisecond.
const NUM_LEVELS: usize = 6;
/// The maximum duration of a sleep
const MAX_DURATION: u64 = 1 << (6 * NUM_LEVELS);
/// Maximum number of timeouts the system can handle concurrently.
const MAX_TIMEOUTS: usize = usize::MAX >> 1;
// ===== impl Timer =====
impl<T> Timer<T>
where T: Park
{
/// Create a new `Timer` instance that uses `park` to block the current
/// thread.
///
/// Once the timer has been created, a handle can be obtained using
/// [`handle`]. The handle is used to create `Sleep` instances.
///
/// Use `default` when constructing a `Timer` using the default `park`
/// instance.
///
/// [`handle`]: #method.handle
pub fn new(park: T) -> Self {
Timer::new_with_now(park, SystemNow::new())
}
}
impl<T, N> Timer<T, N> {
/// Returns a reference to the underlying `Park` instance.
pub fn get_park(&self) -> &T {
&self.park
}
/// Returns a mutable reference to the underlying `Park` instance.
pub fn get_park_mut(&mut self) -> &mut T {
&mut self.park
}
}
impl<T, N> Timer<T, N>
where T: Park,
N: Now,
{
/// Create a new `Timer` instance that uses `park` to block the current
/// thread and `now` to get the current `Instant`.
///
/// Specifying the source of time is useful when testing.
pub fn new_with_now(park: T, mut now: N) -> Self {
let unpark = Box::new(park.unpark());
let levels = (0..NUM_LEVELS)
.map(Level::new)
.collect();
Timer {
inner: Arc::new(Inner::new(now.now(), unpark)),
elapsed: 0,
levels,
park,
now,
}
}
/// Returns a handle to the timer.
///
/// The `Handle` is how `Sleep` instances are created. The `Sleep` instances
/// can either be created directly or the `Handle` instance can be passed to
/// `with_default`, setting the timer as the default timer for the execution
/// context.
pub fn handle(&self) -> Handle {
Handle::new(Arc::downgrade(&self.inner))
}
/// Performs one iteration of the timer loop.
///
/// This function must be called repeatedly in order for the `Timer`
/// instance to make progress. This is where the work happens.
///
/// The `Timer` will use the `Park` instance that was specified in [`new`]
/// to block the current thread until the next `Sleep` instance elapses. One
/// call to `turn` results in at most one call to `park.park()`.
///
/// # Return
///
/// On success, `Ok(Turn)` is returned, where `Turn` is a placeholder type
/// that currently does nothing but may, in the future, have functions add
/// to provide information about the call to `turn`.
///
/// If the call to `park.park()` fails, then `Err` is returned with the
/// error.
///
/// [`new`]: #method.new
pub fn turn(&mut self, max_wait: Option<Duration>) -> Result<Turn, T::Error> {
match max_wait {
Some(timeout) => self.park_timeout(timeout)?,
None => self.park()?,
}
Ok(Turn(()))
}
/// Returns the instant at which the next timeout expires.
fn next_expiration(&self) -> Option<Expiration> {
// Check all levels
for level in 0..NUM_LEVELS {
if let Some(expiration) = self.levels[level].next_expiration(self.elapsed) {
// There cannot be any expirations at a higher level that happen
// before this one.
debug_assert!({
let mut res = true;
for l2 in (level+1)..NUM_LEVELS {
if let Some(e2) = self.levels[l2].next_expiration(self.elapsed) {
if e2.deadline < expiration.deadline {
res = false;
}
}
}
res
});
return Some(expiration);
}
}
None
}
/// Converts an `Expiration` to an `Instant`.
fn expiration_instant(&self, expiration: &Expiration) -> Instant {
self.inner.start + Duration::from_millis(expiration.deadline)
}
/// Run timer related logic
fn process(&mut self) {
let now = ms(self.now.now() - self.inner.start, Round::Down);
loop {
let expiration = match self.next_expiration() {
Some(expiration) => expiration,
None => break,
};
if expiration.deadline > now {
// This expiration should not fire on this tick
break;
}
// Prcess the slot, either moving it down a level or firing the
// timeout if currently at the final (boss) level.
self.process_expiration(&expiration);
self.set_elapsed(expiration.deadline);
}
self.set_elapsed(now);
}
fn set_elapsed(&mut self, when: u64) {
assert!(self.elapsed <= when, "elapsed={:?}; when={:?}", self.elapsed, when);
if when > self.elapsed {
self.elapsed = when;
self.inner.elapsed.store(when, SeqCst);
} else {
assert_eq!(self.elapsed, when);
}
}
fn process_expiration(&mut self, expiration: &Expiration) {
while let Some(entry) = self.pop_entry(expiration) {
if expiration.level == 0 {
let when = entry.when_internal()
.expect("invalid internal entry state");
debug_assert_eq!(when, expiration.deadline);
// Fire the entry
entry.fire(when);
// Track that the entry has been fired
entry.set_when_internal(None);
} else {
let when = entry.when_internal()
.expect("entry not tracked");
let next_level = expiration.level - 1;
self.levels[next_level]
.add_entry(entry, when);
}
}
}
fn pop_entry(&mut self, expiration: &Expiration) -> Option<Arc<Entry>> {
self.levels[expiration.level].pop_entry_slot(expiration.slot)
}
/// Process the entry queue
///
/// This handles adding and canceling timeouts.
fn process_queue(&mut self) {
for entry in self.inner.process.take() {
match (entry.when_internal(), entry.load_state()) {
(None, None) => {
// Nothing to do
}
(Some(when), None) => {
// Remove the entry
self.clear_entry(&entry, when);
}
(None, Some(when)) => {
// Queue the entry
self.add_entry(entry, when);
}
(Some(curr), Some(next)) => {
self.clear_entry(&entry, curr);
self.add_entry(entry, next);
}
}
}
}
fn clear_entry(&mut self, entry: &Arc<Entry>, when: u64) {
// Get the level at which the entry should be stored
let level = self.level_for(when);
self.levels[level].remove_entry(entry, when);
entry.set_when_internal(None);
}
/// Fire the entry if it needs to, otherwise queue it to be processed later.
///
/// Returns `None` if the entry was fired.
fn add_entry(&mut self, entry: Arc<Entry>, when: u64) {
if when <= self.elapsed {
// The entry's deadline has elapsed, so fire it and update the
// internal state accordingly.
entry.set_when_internal(None);
entry.fire(when);
return;
} else if when - self.elapsed > MAX_DURATION {
// The entry's deadline is invalid, so error it and update the
// internal state accordingly.
entry.set_when_internal(None);
entry.error();
return;
}
// Get the level at which the entry should be stored
let level = self.level_for(when);
entry.set_when_internal(Some(when));
self.levels[level].add_entry(entry, when);
debug_assert!({
self.levels[level].next_expiration(self.elapsed)
.map(|e| e.deadline >= self.elapsed)
.unwrap_or(true)
});
}
fn level_for(&self, when: u64) -> usize {
level_for(self.elapsed, when)
}
}
fn level_for(elapsed: u64, when: u64) -> usize {
let masked = elapsed ^ when;
assert!(masked != 0, "elapsed={}; when={}", elapsed, when);
let leading_zeros = masked.leading_zeros() as usize;
let significant = 63 - leading_zeros;
significant / 6
}
impl Default for Timer<ParkThread, SystemNow> {
fn default() -> Self {
Timer::new(ParkThread::new())
}
}
impl<T, N> Park for Timer<T, N>
where T: Park,
N: Now,
{
type Unpark = T::Unpark;
type Error = T::Error;
fn unpark(&self) -> Self::Unpark {
self.park.unpark()
}
fn park(&mut self) -> Result<(), Self::Error> {
self.process_queue();
match self.next_expiration() {
Some(expiration) => {
let now = self.now.now();
let deadline = self.expiration_instant(&expiration);
if deadline > now {
self.park.park_timeout(deadline - now)?;
}
}
None => {
self.park.park()?;
}
}
self.process();
Ok(())
}
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
self.process_queue();
match self.next_expiration() {
Some(expiration) => {
let now = self.now.now();
let deadline = self.expiration_instant(&expiration);
if deadline > now {
self.park.park_timeout(cmp::min(deadline - now, duration))?;
}
}
None => {
self.park.park_timeout(duration)?;
}
}
self.process();
Ok(())
}
}
impl<T, N> Drop for Timer<T, N> {
fn drop(&mut self) {
// Shutdown the stack of entries to process, preventing any new entries
// from being pushed.
self.inner.process.shutdown();
}
}
// ===== impl Inner =====
impl Inner {
fn new(start: Instant, unpark: Box<Unpark>) -> Inner {
Inner {
num: AtomicUsize::new(0),
elapsed: AtomicU64::new(0),
process: entry::AtomicStack::new(),
start,
unpark,
}
}
fn elapsed(&self) -> u64 {
self.elapsed.load(SeqCst)
}
/// Increment the number of active timeouts
fn increment(&self) -> Result<(), Error> {
let mut curr = self.num.load(SeqCst);
loop {
if curr == MAX_TIMEOUTS {
return Err(Error::at_capacity());
}
let actual = self.num.compare_and_swap(curr, curr + 1, SeqCst);
if curr == actual {
return Ok(());
}
curr = actual;
}
}
/// Decrement the number of active timeouts
fn decrement(&self) {
let prev = self.num.fetch_sub(1, SeqCst);
debug_assert!(prev <= MAX_TIMEOUTS);
}
fn queue(&self, entry: &Arc<Entry>) -> Result<(), Error> {
if self.process.push(entry)? {
// The timer is notified so that it can process the timeout
self.unpark.unpark();
}
Ok(())
}
fn normalize_deadline(&self, deadline: Instant) -> u64 {
if deadline < self.start {
return 0;
}
ms(deadline - self.start, Round::Up)
}
}
impl fmt::Debug for Inner {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Inner")
.finish()
}
}
enum Round {
Up,
Down,
}
/// Convert a `Duration` to milliseconds, rounding up and saturating at
/// `u64::MAX`.
///
/// The saturating is fine because `u64::MAX` milliseconds are still many
/// million years.
#[inline]
fn ms(duration: Duration, round: Round) -> u64 {
const NANOS_PER_MILLI: u32 = 1_000_000;
const MILLIS_PER_SEC: u64 = 1_000;
// Round up.
let millis = match round {
Round::Up => (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI,
Round::Down => duration.subsec_nanos() / NANOS_PER_MILLI,
};
duration.as_secs().saturating_mul(MILLIS_PER_SEC).saturating_add(millis as u64)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_level_for() {
for pos in 1..64 {
assert_eq!(0, level_for(0, pos), "level_for({}) -- binary = {:b}", pos, pos);
}
for level in 1..5 {
for pos in level..64 {
let a = pos * 64_usize.pow(level as u32);
assert_eq!(level, level_for(0, a as u64),
"level_for({}) -- binary = {:b}", a, a);
if pos > level {
let a = a - 1;
assert_eq!(level, level_for(0, a as u64),
"level_for({}) -- binary = {:b}", a, a);
}
if pos < 64 {
let a = a + 1;
assert_eq!(level, level_for(0, a as u64),
"level_for({}) -- binary = {:b}", a, a);
}
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
use std::time::Instant;
/// Returns `Instant` values representing the current instant in time.
///
/// This allows customizing the source of time which is especially useful for
/// testing.
pub trait Now {
/// Returns an instant corresponding to "now".
fn now(&mut self) -> Instant;
}
/// Returns the instant corresponding to now using a monotonic clock.
#[derive(Debug)]
pub struct SystemNow(());
impl SystemNow {
/// Create a new `SystemNow`.
pub fn new() -> SystemNow {
SystemNow(())
}
}
impl Now for SystemNow {
fn now(&mut self) -> Instant {
Instant::now()
}
}
+82
View File
@@ -0,0 +1,82 @@
use Error;
use timer::{Handle, Entry};
use futures::Poll;
use std::sync::Arc;
use std::time::Instant;
/// Registration with a timer.
///
/// The association between a `Sleep` instance and a timer is done lazily in
/// `poll`
#[derive(Debug)]
pub(crate) struct Registration {
entry: Arc<Entry>,
}
impl Registration {
pub fn new(deadline: Instant) -> Registration {
fn is_send<T: Send + Sync>() {}
is_send::<Registration>();
match Handle::try_current() {
Ok(handle) => Registration::new_with_handle(deadline, handle),
Err(_) => Registration::new_error(),
}
}
pub fn new_with_handle(deadline: Instant, handle: Handle) -> Registration {
let inner = match handle.inner() {
Some(inner) => inner,
None => return Registration::new_error(),
};
// Increment the number of active timeouts
if inner.increment().is_err() {
return Registration::new_error();
}
let when = inner.normalize_deadline(deadline);
if when <= inner.elapsed() {
// The deadline has already elapsed, ther eis no point creating the
// structures.
return Registration {
entry: Arc::new(Entry::new_elapsed(handle)),
};
}
let entry = Arc::new(Entry::new(when, handle));
if inner.queue(&entry).is_err() {
// The timer has shutdown, transition the entry to the error state.
entry.error();
}
Registration { entry }
}
pub fn reset(&self, deadline: Instant) {
Entry::reset(&self.entry, deadline);
}
fn new_error() -> Registration {
let entry = Arc::new(Entry::new_error());
Registration { entry }
}
pub fn is_elapsed(&self) -> bool {
self.entry.is_elapsed()
}
pub fn poll_elapsed(&self) -> Poll<(), Error> {
self.entry.poll_elapsed()
}
}
impl Drop for Registration {
fn drop(&mut self) {
Entry::cancel(&self.entry);
}
}