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