]> Dogcows Code - chaz/yoink/blob - src/moof/timer.cc
use only triangles; no quads
[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.valid(); ++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.valid(); ++it)
153 {
154 timer* timer = (*it).second;
155 if (timer->is_expired()) timer->fire();
156
157 if (it.end()) break;
158 }
159 }
160
161
162 #if USE_CLOCK_GETTIME
163
164 // Since the monotonic clock will provide us with the time since the
165 // computer started, the number of seconds since that time could easily
166 // become so large that it cannot be accurately stored in a float (even
167 // with as little two days uptime), therefore we need to start from a more
168 // recent reference (when the program starts). Of course this isn't much
169 // of an issue if scalar is a double-precision number.
170
171 static time_t set_reference()
172 {
173 struct timespec ts;
174
175 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
176 {
177 return 0;
178 }
179
180 return ts.tv_sec;
181 }
182
183 static const time_t reference_ = set_reference();
184
185
186 scalar timer::ticks()
187 {
188 struct timespec ts;
189
190 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
191 ASSERT(result == 0 && "cannot access clock");
192
193 return scalar(ts.tv_sec - reference_) +
194 scalar(ts.tv_nsec) / 1000000000.0;
195 }
196
197 void timer::sleep(scalar seconds, mode mode)
198 {
199 struct timespec ts;
200 int ret;
201
202 if (mode == absolute) seconds -= ticks();
203 ts.tv_sec = time_t(seconds);
204 ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0);
205
206 do
207 {
208 ret = nanosleep(&ts, &ts);
209 }
210 while (ret == -1 && errno == EINTR);
211 }
212
213
214 #else // ! USE_CLOCK_GETTIME
215
216
217 // If we don't have posix timers, we'll have to use a different timing
218 // method. SDL only promises centisecond accuracy, but that's better than
219 // a kick in the pants.
220
221 scalar timer::ticks()
222 {
223 Uint32 ms = SDL_GetTicks();
224 return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0;
225 }
226
227 void timer::sleep(scalar seconds, mode mode)
228 {
229 if (mode == absolute) seconds -= ticks();
230 SDL_Delay(Uint32(clamp(int(seconds * 1000.0), 0, 1000)));
231 }
232
233 #endif // USE_CLOCK_GETTIME
234
235
236 } // namespace moof
237
This page took 0.037963 seconds and 4 git commands to generate.