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
//! 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>, } /// 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, } } 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 } /// Build the configured `Timer` and return a handle to it. pub fn build(self) -> Timer { timer::build(self) } }