rotor_tools/
timer.rs

1//! Time utitities
2use std::time::Duration;
3use rotor::{Machine, Scope, Response, EventSet, GenericScope, Time};
4use rotor::void::{Void, unreachable};
5
6/// Ticker state machine
7///
8/// The structure implements `rotor::Machine` but exposes a simpler protocol
9/// that has just one method which is called when timer expires.
10///
11/// The `Ticker` machine also ensures that there are no spurious events.
12pub struct Ticker<M: Timer> {
13    deadline: Time,
14    machine: M,
15}
16
17/// Interval state machine
18///
19/// It's the state machine used for ticker that wakes up with fixed intervals
20pub struct Interval<M: SimpleTimer>(Duration, M);
21
22/// A convenience type for declaring state machines
23pub type IntervalFunc<C> = Ticker<Interval<Box<FnMut(&mut Scope<C>) + Send>>>;
24
25/// A protocol for the state machine that put into the `Ticker`
26pub trait Timer {
27    type Context;
28
29    /// Called when time elapsed
30    fn timeout(self, scope: &mut Scope<Self::Context>) -> Self;
31
32    /// Calculates the next wakeup time
33    ///
34    /// `scheduled` -- time when event had to occur
35    ///
36    /// There are two options to calculate the time. If you just need to
37    /// run something on occasion use simply:
38    /// ```ignore
39    /// scope.now() + Duration::new(interval, )
40    /// ```
41    ///
42    /// Or if you need to run strict number of times and as close as possible
43    /// to the multiple of the interval time. You may want:
44    /// ```ignore
45    /// scheduled + Duration::new(interval, 0)
46    /// ```
47    ///
48    /// Note, in both cases mio will run timeout handler on the next tick
49    /// of the timer, which means +200 ms by default.
50    fn next_wakeup_time(&self, scheduled: Time,
51        scope: &mut Scope<Self::Context>)
52        -> Time;
53}
54
55/// The timer trait used in the `Ticker<Interval<T>>`
56pub trait SimpleTimer {
57    type Context;
58
59    /// Called when time elapsed
60    fn timeout(self, scope: &mut Scope<Self::Context>) -> Self;
61}
62
63impl<T: Timer> Ticker<T> {
64    pub fn new(scope: &mut Scope<T::Context>, machine: T)
65        -> Response<Ticker<T>, Void>
66    {
67        let next = machine.next_wakeup_time(scope.now(), scope);
68        Response::ok(Ticker {
69            deadline: next,
70            machine: machine,
71        }).deadline(next)
72    }
73}
74
75impl<M: Timer> Machine for Ticker<M> {
76    type Context = M::Context;
77    type Seed = Void;
78    fn create(seed: Self::Seed, _scope: &mut Scope<Self::Context>)
79        -> Response<Self, Void>
80    {
81        unreachable(seed);
82    }
83    fn ready(self, _events: EventSet, _scope: &mut Scope<Self::Context>)
84        -> Response<Self, Self::Seed>
85    {
86        // Spurious event
87        let deadline = self.deadline;
88        Response::ok(self).deadline(deadline)
89    }
90    fn spawned(self, _scope: &mut Scope<Self::Context>)
91        -> Response<Self, Self::Seed>
92    {
93        unreachable!();
94    }
95    fn timeout(self, scope: &mut Scope<Self::Context>)
96        -> Response<Self, Self::Seed>
97    {
98        let now = scope.now();
99        if now >= self.deadline {
100            let newm = self.machine.timeout(scope);
101            let next = newm.next_wakeup_time(self.deadline, scope);
102            Response::ok(Ticker {
103                deadline: next,
104                machine: newm,
105            }).deadline(next)
106        } else {
107            // Spurious timeout
108            // TODO(tailhook) should not happen when we get rid of
109            // scope.timeout_ms()
110            let deadline = self.deadline;
111            Response::ok(self).deadline(deadline)
112        }
113    }
114    fn wakeup(self, _scope: &mut Scope<Self::Context>)
115        -> Response<Self, Self::Seed>
116    {
117        // Spurious wakeup
118        let deadline = self.deadline;
119        Response::ok(self).deadline(deadline)
120    }
121}
122
123impl<T: SimpleTimer> Timer for Interval<T> {
124    type Context = T::Context;
125    fn timeout(self, scope: &mut Scope<Self::Context>) -> Self {
126        Interval(self.0, self.1.timeout(scope))
127    }
128    fn next_wakeup_time(&self, scheduled: Time,
129        scope: &mut Scope<Self::Context>)
130        -> Time
131    {
132        // Try to minimize the drift
133        let goal = scheduled + self.0;
134        if scope.now() > goal {
135            // But if we are a way too late, just use current time
136            return scope.now() + self.0;
137        } else {
138            return goal;
139        }
140    }
141}
142
143impl<C> SimpleTimer for Box<FnMut(&mut Scope<C>) + Send> {
144    type Context = C;
145    fn timeout(mut self, scope: &mut Scope<Self::Context>) -> Self {
146        self(scope);
147        self
148    }
149}
150
151/// A helper function to create intervals from closures
152pub fn interval_func<C, F>(scope: &mut Scope<C>, interval: Duration, fun: F)
153    -> Response<IntervalFunc<C>, Void>
154    where F: FnMut(&mut Scope<C>) + 'static + Send
155{
156    Ticker::new(scope, Interval(interval, Box::new(fun)))
157}