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}