1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
//! Timer facilities for Tokio
//!
//! The default timer implementation is a hashed timing wheel. This structure
//! provides the best runtime characteristics for the majority of network
//! application patterns **as long as it is correctly configured**. A hashed
//! timing wheel's worst case is `O(n)` where `n` is the number of pending
//! timeouts.
//!
//! Most useful functions are on [`Timer`](struct.Timer.html).
//!
//! ## Example
//!
//! Here is a simple example of how to use the timer.
//!
//! ```rust
//! extern crate tokio_timer;
//! extern crate futures;
//!
//! use tokio_timer::*;
//! use futures::*;
//! use std::time::*;
//!
//! pub fn main() {
//!     // Create a new timer with default settings. While this is the easiest way
//!     // to get a timer, usually you will want to tune the config settings for
//!     // your usage patterns.
//!     let timer = Timer::default();
//!
//!     // Set a timeout that expires in 500 milliseconds
//!     let sleep = timer.sleep(Duration::from_millis(500));
//!
//!     // Use the `Future::wait` to block the current thread until `Sleep`
//!     // future completes.
//!     //
//!     sleep.wait();
//! }
//! ```
//!
//! ## Hashed Timing Wheel
//!
//! The hashed timing wheel timer is a coarse grained timer that is optimized
//! for cases where the timeout range is relatively uniform and high precision
//! is not needed. These requirements are very common with network related
//! applications as most timeouts tend to be a constant range (for example, 30
//! seconds) and timeouts are used more as a safe guard than for high
//! precision.
//!
//! The timer is inspired by the [paper by Varghese and
//! Lauck](http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf).
//!
//! A hashed wheel timer is implemented as a vector of "slots" that represent
//! time slices. The default slot size is 100ms. As time progresses, the timer
//! walks over each slot and looks in the slot to find all timers that are due
//! to expire. When the timer reaches the end of the vector, it starts back at
//! the beginning.
//!
//! Given the fact that the timer operates in ticks, a timeout can only be as
//! precise as the tick duration. If the tick size is 100ms, any timeout
//! request that falls within that 100ms slot will be triggered at the same
//! time.
//!
//! A timer is assigned to a slot by taking the expiration instant and
//! assigning it to a slot, factoring in wrapping. When there are more than one
//! timeouts assigned to a given slot, they are stored in a linked list.
//!
//! This structure allows constant time timer operations **as long as timeouts
//! don't collide**. In other words, if two timeouts are set to expire at
//! exactly `num-slots * tick-duration` time apart, they will be assigned to
//! the same bucket.
//!
//! The best way to avoid collisions is to ensure that no timeout is set that
//! is for greater than `num-slots * tick-duration` into the future.
//!
//! A timer can be configured with `Builder`.
//!
//! ## Runtime details
//!
//! When creating a timer, a thread is spawned. The timing details are managed
//! on this thread. When `Timer::set_timeout` is called, a request is sent to
//! the thread over a bounded channel.
//!
//! All storage needed to run the timer is pre-allocated, which means that the
//! timer system is able to run without any runtime allocations. The one
//! exception would be if the timer's `max_capacity` is larger than the
//! `initial_capacity`, in which case timeout storage is allocated in chunks as
//! needed. Timeout storage can grow but never shrink.

#![deny(warnings, missing_docs, missing_debug_implementations)]

#[macro_use]
extern crate futures;
extern crate slab;

mod interval;
mod mpmc;
mod timer;
mod wheel;
mod worker;

pub use interval::Interval;
pub use timer::{Sleep, Timer, Timeout, TimeoutStream, TimerError, TimeoutError};

use std::cmp;
use std::time::Duration;

/// Configures and builds a `Timer`
///
/// A `Builder` is obtained by calling `wheel()`.
#[derive(Debug)]
pub struct Builder {
    tick_duration: Option<Duration>,
    num_slots: Option<usize>,
    initial_capacity: Option<usize>,
    max_capacity: Option<usize>,
    max_timeout: Option<Duration>,
    channel_capacity: Option<usize>,
    thread_name: Option<String>,
}

/// Configure and build a `Timer` backed by a hashed wheel.
pub fn wheel() -> Builder {
    Builder {
        tick_duration: None,
        num_slots: None,
        initial_capacity: None,
        max_capacity: None,
        max_timeout: None,
        channel_capacity: None,
        thread_name: None,
    }
}

impl Builder {
    fn get_tick_duration(&self) -> Duration {
        self.tick_duration.unwrap_or(Duration::from_millis(100))
    }

    /// Set the timer tick duration.
    ///
    /// See the crate docs for more detail.
    ///
    /// Defaults to 100ms.
    pub fn tick_duration(mut self, tick_duration: Duration) -> Self {
        self.tick_duration = Some(tick_duration);
        self
    }

    fn get_num_slots(&self) -> usize {
        // About 6 minutes at a 100 ms tick size
        self.num_slots.unwrap_or(4_096)
    }

    /// Set the number of slots in the timer wheel.
    ///
    /// The number of slots must be a power of two.
    ///
    /// See the crate docs for more detail.
    ///
    /// Defaults to 4,096.
    pub fn num_slots(mut self, num_slots: usize) -> Self {
        self.num_slots = Some(num_slots);
        self
    }

    fn get_initial_capacity(&self) -> usize {
        let cap = self.initial_capacity.unwrap_or(256);
        cmp::max(cap, self.get_channel_capacity())
    }

    /// Set the initial capacity of the timer
    ///
    /// The timer's timeout storage vector will be initialized to this
    /// capacity. When the capacity is reached, the storage will be doubled
    /// until `max_capacity` is reached.
    ///
    /// Default: 128
    pub fn initial_capacity(mut self, initial_capacity: usize) -> Self {
        self.initial_capacity = Some(initial_capacity);
        self
    }

    fn get_max_capacity(&self) -> usize {
        self.max_capacity.unwrap_or(4_194_304)
    }

    /// Set the max capacity of the timer
    ///
    /// The timer's timeout storage vector cannot get larger than this capacity
    /// setting.
    ///
    /// Default: 4,194,304
    pub fn max_capacity(mut self, max_capacity: usize) -> Self {
        self.max_capacity = Some(max_capacity);
        self
    }

    fn get_max_timeout(&self) -> Duration {
        let default = self.get_tick_duration() * self.get_num_slots() as u32;
        self.max_timeout.unwrap_or(default)
    }

    /// Set the max timeout duration that can be requested
    ///
    /// Setting the max timeout allows preventing the case of timeout collision
    /// in the hash wheel and helps guarantee optimial runtime characteristics.
    ///
    /// See the crate docs for more detail.
    ///
    /// Defaults to `num_slots * tick_duration`
    pub fn max_timeout(mut self, max_timeout: Duration) -> Self {
        self.max_timeout = Some(max_timeout);
        self
    }

    fn get_channel_capacity(&self) -> usize {
        self.channel_capacity.unwrap_or(128)
    }

    /// Set the timer communication channel capacity
    ///
    /// The timer channel is used to dispatch timeout requests to the timer
    /// thread. In theory, the timer thread is able to drain the channel at a
    /// very fast rate, however it is always possible for the channel to fill
    /// up.
    ///
    /// This setting indicates the max number of timeout requests that are able
    /// to be buffered before timeout requests are rejected.
    ///
    /// Defaults to 128
    pub fn channel_capacity(mut self, channel_capacity: usize) -> Self {
        self.channel_capacity = Some(channel_capacity);
        self
    }

    /// Set the name for the spawned thread.
    ///
    /// See also the runtime details in crate docs.
    ///
    /// Defaults to "tokio-timer".
    pub fn thread_name<S: Into<String>>(mut self, name: S) -> Self {
        self.thread_name = Some(name.into());
        self
    }

    /// Build the configured `Timer` and return a handle to it.
    pub fn build(self) -> Timer {
        timer::build(self)
    }
}