configuration cleanup and bugfixes
[chaz/yoink] / src / moof / timer.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include "config.h"
13
14 #include <cerrno>
15 #include <ctime>
16 #include <limits>
17
18 #include <SDL/SDL.h>
19
20 #include "debug.hh"
21 #include "timer.hh"
22
23
24 namespace moof {
25
26
27 scalar timer::next_expiration_ = std::numeric_limits<scalar>::max();
28 hash<unsigned,timer*,hash_function> timer::timers_;
29
30
31 unsigned timer::new_identifier()
32 {
33 static unsigned id = 1;
34 return id++;
35 }
36
37
38 void timer::init(const function& function, scalar seconds, mode mode)
39 {
40 invalidate();
41
42 mode_ = mode;
43
44 if (mode_ != invalid)
45 {
46 function_ = function;
47
48 if (mode == absolute)
49 {
50 absolute_ = seconds;
51 }
52 else
53 {
54 absolute_ = seconds - ticks();
55 interval_ = seconds;
56 }
57
58 id_ = new_identifier();
59 timers_.insert(std::pair<unsigned,timer*>(id_, this));
60
61 if (absolute_ < next_expiration_) next_expiration_ = absolute_;
62 }
63 }
64
65
66 bool timer::is_valid() const
67 {
68 return mode_ != invalid;
69 }
70
71 void timer::invalidate()
72 {
73 if (mode_ != invalid)
74 {
75 timers_.erase(id_);
76 mode_ = invalid;
77
78 if (is_equal(absolute_, next_expiration_))
79 {
80 next_expiration_ = find_next_expiration();
81 }
82 }
83 }
84
85
86 void timer::fire()
87 {
88 scalar t = ticks();
89
90 if (function_) function_(*this, t);
91
92 if (is_repeating())
93 {
94 scalar absolute = absolute_;
95
96 if (is_equal(absolute_, t, 1.0)) absolute_ += interval_;
97 else absolute_ = interval_ + t;
98
99 if (is_equal(absolute, next_expiration_))
100 {
101 next_expiration_ = find_next_expiration();
102 }
103 }
104 else
105 {
106 invalidate();
107 }
108 }
109
110
111 scalar timer::find_next_expiration()
112 {
113 scalar next_fire = std::numeric_limits<scalar>::max();
114
115 hash<unsigned,timer*,hash_function>::iterator it;
116 for (it = timers_.begin(); it != timers_.end(); ++it)
117 {
118 scalar absolute = (*it).second->absolute_;
119 if (absolute < next_fire) next_fire = absolute;
120 }
121
122 return next_fire;
123 }
124
125
126 scalar timer::seconds_remaining() const
127 {
128 return absolute_ - ticks();
129 }
130
131 bool timer::is_expired() const
132 {
133 return seconds_remaining() < 0.0;
134 }
135
136 bool timer::is_repeating() const
137 {
138 return mode_ == repeat;
139 }
140
141
142 void timer::fire_expired_timers()
143 {
144 fire_expired_timers(ticks());
145 }
146
147 void timer::fire_expired_timers(scalar t)
148 {
149 if (next_expiration_ > t) return;
150
151 hash<unsigned,timer*,hash_function>::iterator it;
152 for (it = timers_.begin(); it != timers_.end(); ++it)
153 {
154 timer* timer = (*it).second;
155 if (timer->is_expired()) timer->fire();
156 }
157 }
158
159
160 #if USE_CLOCK_GETTIME
161
162 // Since the monotonic clock will provide us with the time since the
163 // computer started, the number of seconds since that time could easily
164 // become so large that it cannot be accurately stored in a float (even
165 // with as little two days uptime), therefore we need to start from a more
166 // recent reference (when the program starts). Of course this isn't much
167 // of an issue if scalar is a double-precision number.
168
169 static time_t set_reference()
170 {
171 struct timespec ts;
172
173 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
174 {
175 return 0;
176 }
177
178 return ts.tv_sec;
179 }
180
181 static const time_t reference_ = set_reference();
182
183
184 scalar timer::ticks()
185 {
186 struct timespec ts;
187
188 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
189 ASSERT(result == 0 && "cannot access clock");
190
191 return scalar(ts.tv_sec - reference_) +
192 scalar(ts.tv_nsec) / 1000000000.0;
193 }
194
195 void timer::sleep(scalar seconds, mode mode)
196 {
197 struct timespec ts;
198 int ret;
199
200 if (mode == absolute) seconds -= ticks();
201 ts.tv_sec = time_t(seconds);
202 ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0);
203
204 do
205 {
206 ret = nanosleep(&ts, &ts);
207 }
208 while (ret == -1 && errno == EINTR);
209 }
210
211
212 #else // ! USE_CLOCK_GETTIME
213
214
215 // If we don't have posix timers, we'll have to use a different timing
216 // method. SDL only promises centisecond accuracy, but that's better than
217 // a kick in the pants.
218
219 scalar timer::ticks()
220 {
221 Uint32 ms = SDL_GetTicks();
222 return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0;
223 }
224
225 void timer::sleep(scalar seconds, mode mode)
226 {
227 if (mode == absolute) seconds -= ticks();
228 SDL_Delay(Uint32(clamp(int(seconds * 1000.0), 0, 1000)));
229 }
230
231 #endif // USE_CLOCK_GETTIME
232
233
234 } // namespace moof
235
This page took 0.038367 seconds and 4 git commands to generate.