tokio_timer/
lib.rs

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