Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 178 lines (122 sloc) 6.366 kB
4e94bec @lehoff Initial commit. Very incomplete.
authored
1 # Chronos - a timer utility for Erlang.
2
3 Erlang comes with some good utilities for timers, but there are some
3329067 Added more explanation in the README.md file
Torben Hoffmann authored
4 shortcomings that might become an issue for you as they did for me.
4e94bec @lehoff Initial commit. Very incomplete.
authored
5
6 Chronos tries to hide the book keeping from the user in a way that
7 will be very familiar to those who have tried to implement protocols
3329067 Added more explanation in the README.md file
Torben Hoffmann authored
8 from the telecommunications realm.
4e94bec @lehoff Initial commit. Very incomplete.
authored
9
10 In addition to the abstraction the Chronos distribution also shows how
11 to design the APIs of your code in such a way that it makes it easier
12 to test the code. This part requires the use of the meck application
13 by Adam Lindberg or some serious manual hacking... I am going to show
e6decce Re-organised README to have the overview of existing timer solutions …
Torben Hoffmann authored
14 the meck way of doing it. See the `ping_test` module in the `examples`
15 directory.
16
17 Abstracting time as it is suggested (with or with Chronos) will give
18 you a design where you can test how timers work very fast. You can
19 trust things will work in real life since Chronos comes with a test
20 suite that shows that it does what you would expect. The burden on you
21 is then to write a test that shows that your component works as it
22 should for a sequence of events where some of them happens to be
23 timers expiring. And you do not have to wait for the timers to expire
24 since time has been abstracted.
25
26 Below the description of how to use Chronos there is a brief
4e94bec @lehoff Initial commit. Very incomplete.
authored
27 overview of what the existing timer solutions has to offer so you can
28 make an informed choice about which timer solution fits your problem
29 the best.
30
31
3329067 Added more explanation in the README.md file
Torben Hoffmann authored
32 # The Chronos approach to timers
4e94bec @lehoff Initial commit. Very incomplete.
authored
33
3329067 Added more explanation in the README.md file
Torben Hoffmann authored
34 The design of Chronos was influenced by the problems with the existing
35 timer solutions and shaped by the needs when implementing telecom
36 protocols, which uses timers extensively.
37
38 ## Timer servers
39
40 Instead of having a single global timer server Chronos allows you to
41 create as many or as few timer serves as you see fit.
42
43 start_link(ServerName)
44
45 will start a new timer server where
46
47 ServerName :: term().
48
49 ## Timers
50
51 Once you have started a timer server you can start timers using
52
53 start_timer(ServerName, TimerName, Timeout, Callback)
54
55 where
56
57 ServerName :: term()
58 TimerName :: term()
59 Timeout :: pos_integer()
60 Callback :: {module(), atom(), [term[]]}
61
62 The timer will be given a unique name - if you start the timer again
63 it will be restarted - and when the `Timeout` ms has passed the
64 `Callback` function will be called.
65
66 In some cases you want to cancel a timer and for that you can use
67
68 `stop_timer(ServerName, TimerName)`
69
70 In this case the `Callback` function will not be called.
71
72 Chronos keeps track of all the running timers so your application code
73 can concentrate on what it is supposed to do without having to do
74 tedious book keeping.
75
76 ## Testing with Chronos
77
78 Getting rid of tedious book keeping is not the only thing you get from
79 using Chronos. By putting all your timers in the hands of Chronos you
80 get a set-up that is very easy to mock so that you can abstract time
81 out of your tests.
82
83 One way of doing this is to provide a `timer_expiry` function as part
84 of the API for the component you are creating. One of the arguments
85 should be the name of the timer and if you start more instances of the
86 component you need to have the name of the component in the call as
87 well.
88
89 timer_expiry(TimerName) ->
90 gen_server:call(?SERVER, {timer_expiry, TimerName}).
91
92 In the code you can request a timer like this:
93
94 chronos:start_timer(ServerName,
95 timer_4,
96 {my_mod, timer_expiry, [timer_4]})
97
98 and then handling the timeout becomes very simple:
99
100 handle_call({timer_expiry, timer_4}, _From, State) ->
101 ...
102
103 That is the basic set-up and while testing you have to mock
104 Chronos. This is easy to do with the meck application and should be
105 quite simple to do with any mocking library.
106
107 So you ensure that you have control over chronos:
108
109 meck:new(chronos),
110 meck:expect(chronos, start_timer,
111 fun(_,_) -> 42 end)
112
113 As part of the test you check that the timer was requsted to start:
114
115 meck:called(chronos, start_timer, [my_server, timer_4])
116
117 And when you come to the point in the test where you want to see the
118 effects of the timer expiry you simply call
119
120 my_mod:timer_expiry(timer_4)
121
122 This approach lends itself well to property based testing and unit
123 testing.
124
e6decce Re-organised README to have the overview of existing timer solutions …
Torben Hoffmann authored
125 # Existing timer solutions
126
127 ## The timer module from the stdlib
128
129 This is an excellent module in terms of abstraction: it provides all
130 the functionality you would want in order to start and stop timers.
131
132 Unfortunately there can only be one timer server for each Erlang node
133 and that is that it can very easily become overloaded - see the
134 [http://erlang.org/doc/efficiency_guide/commoncaveats.html] entry on
135 the timer module.
136
137 ## Using the erlang modules timer functions
138
139 As the Efficiency Guide states the `erlang:send_after/3` and
140 `erlang:start_timer/3` are much more efficient than the timer module,
141 but using them directly requires some book keeping which can clutter
142 your code uncessarily.
143
144 ## Using the OTP timers
145
146 I am assuming that you use Erlang/OTP to develop your software - if
147 not you can skip this section! And in that case you probably never got
148 to this line since the Chronos abstraction is too high level for your
149 taste...
150
151 For `gen_server` and `gen_fsm` you can specify a timer in the result
152 tuple from your handler functions, e.g., for `gen_fsm` you can return
153 `{next_state,NextStateName,NewStateData,Timeout}`. If the `gen_server`
154 does not receive another message within `Timeout` milliseconds a timeout
155 event will happen. The problem with this approach is that if any
156 message arrives the timer is cancelled and in many cases you want to
157 see a specific message before you cancel the timer or you want to have
158 multiple timers running.
159
160 You can have multiple timers with `gen_fsm` by using the
161 `gen_fsm:start_timer/2` function. The down side is that you have to do
162 book keeping of timer references if you want to cancel the timer. This
163 is similar to using the erlang module's timers mentioned above.
164
165 The downside is that there is not equivalent of
166 `gen_fsm:start_timer/2` for `gen_server` so for that you have to use
167 one of the other solutions.
168
169
170
3329067 Added more explanation in the README.md file
Torben Hoffmann authored
171 # FAQ
172
173 ## Q: How can you be sure that the timers will do the right thing?
174
175 A: Chronos comes with a property based testing suite that validates
176 that the timers can be started, stopped and restarted as expected
177 and that they expire as expected.
Something went wrong with that request. Please try again.