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