74acc1c203a9bc45b7cb894835e55b9e760162fc
[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(scalar t)
143 {
144 if (next_expiration_ > t) return;
145
146 hash<unsigned,timer*,hash_function>::iterator it;
147 for (it = timers_.begin(); it.valid(); ++it)
148 {
149 timer* timer = (*it).second;
150 if (timer->is_expired()) timer->fire();
151
152 if (it.end()) break;
153 }
154 }
155
156
157 #if USE_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) * SCALAR(0.000000001);
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)) * SCALAR(1000000000.0));
200
201 do
202 {
203 ret = nanosleep(&ts, &ts);
204 }
205 while (ret == -1 && errno == EINTR);
206 }
207
208
209 #else // ! USE_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) * SCALAR(0.001);
220 }
221
222 void timer::sleep(scalar seconds, mode mode)
223 {
224 if (mode == absolute) seconds -= ticks();
225 SDL_Delay(Uint32(clamp(Uint32(seconds * SCALAR(1000.0)), 0, 1000)));
226 }
227
228 #endif // USE_CLOCK_GETTIME
229
230
231 } // namespace moof
232
This page took 0.037877 seconds and 3 git commands to generate.