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

//! Data structures and runtimes for timer management.
//! 
//! This library provides various data structures for managing coarse grain timers.
//! 
//! #### Data Structures
//! 
//! **Overview**
//! 
//! We provide various data structures for timer management, each with their own tradeoffs and suited for different application.
//! 
//! * For applications that set shorted lived timeouts, all around the same duration, consider using a `HashedWheel`, as it will
//! require minimal bookkeeping at runtime for operations such as ticking, or removing expired timers.
//!     * Network communication
//!     * Cache for short lived items
//! 
//! * For applications that set long lived timeouts, or timeouts spread across many different durations, considuer using a `HierarchicalWheel`, as
//! it will accept a variety of different durations, without loss of precision.
//!     * Cache for long lived items
//!     * Varied timeouts from user input
//! 
//! **Hashed Wheel**
//! 
//! Stores all timers in a fixed number of slots (`num_slots`), where each slot represents some fixed period of time (`tick_duration`).
//! The slots we maintain are used as a circular buffer, where after each tick, we advance our position in the buffer. Each tick occurrs
//! after approximately `tick_duration` time has passed.
//! 
//! **Note**: One slot could have multiple timers of differing durations, these timers will be sorted based on tick expiration. However, if
//! the wheel is configured such that one slot contains timers expiring at different ticks, insertion time will fall back to O(n) instead of O(1).
//! By default, `max_timeout` is set equal to `tick_duration` * `num_slots` - `1 ns`, to ensure that all timers inserted will be O(1) inserts, though
//! you can change this if required.
//! 
//! **Hierarchical Wheel**
//! 
//! *Not Implemented Yet*
//! 
//! #### Runtimes
//! 
//! **Overview**
//! 
//! We provide various runtimes on top of the base data structures for timer management, each being generic over the type of `Pendulum` that you choose.
//! 
//! Runtimes are useful because they allow you to not care about how the underlying data structure is getting ticked, or when we should check for an expired timer.
//! 
//! **Futures**
//! 
//! Runs the actual `Pendulum` in a separate thread that handles accepting requests for new timers, as well as making sure the `Pendulum` is ticked correctly, and
//! finally notifications for expired timeouts.
//! 
//! Since the futures library is based around the concept of readiness, via `Task`, we can have callers doing other useful work and make it so that callers aren't
//! continuously checking if a timer is up, only when it is actually up with the background timer thread signal readiness, so that the `Future`s library will
//! poll the timer again.
//! 
//! **Note**: Timer bounds (on both the duration and capacity), is checked before returning a `Future`/`Stream` from the `Timer` object, so the only `Error` being
//! returned from either of those objects is related to timer expiration, which makes it easy to integrate the error handling into your application correctly.
//! 
//! ## Hashed Wheel Example:
//! 
//! ```rust
//! extern crate pendulum;
//! 
//! use std::time::Duration;
//! use std::thread;
//! 
//! use pendulum::{Pendulum, HashedWheelBuilder};
//! 
//! #[derive(Debug, PartialEq, Eq)]
//! struct SomeData(usize);
//! 
//! fn main() {
//!     // Create a pendulum with mostly default configration
//!     let mut wheel = HashedWheelBuilder::default()
//!         // Tick duration defines the resolution for our timer (all timeouts will be a multiple of this)
//!         .with_tick_duration(Duration::from_millis(100))
//!         .build();
//! 
//!     // Insert a timeout and store the token, we can use this to cancel the timeout
//!     let token = wheel.insert_timeout(Duration::from_millis(50), SomeData(5)).unwrap();
//! 
//!     // Tick our wheel after the given duration (100 ms)
//!     thread::sleep(wheel.tick_duration());
//! 
//!     // Tell the wheel that it can perform a tick
//!     wheel.tick();
//! 
//!     // Retrieve any expired timeouts
//!     while let Some(timeout) = wheel.expired_timeout() {
//!         assert_eq!(SomeData(5), timeout);
//!     }
//!     
//!     // If we tried to remove the timeout using the token, we get None (already expired)
//!     assert_eq!(None, wheel.remove_timeout(token));
//! }
//! ```

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

#[cfg(feature = "future")]
extern crate crossbeam;
#[cfg(feature = "future")]
extern crate futures;

pub mod error;

#[cfg(feature = "future")]
pub mod future;

mod pendulum;
mod wheel;

pub use pendulum::{Pendulum, Token};
pub use wheel::{HashedWheel, HashedWheelBuilder};