testing improved runloop scheduling
[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_event_ = 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_event_) next_event_ = 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_event_))
79 {
80 next_event_ = find_next_event();
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_event_))
100 {
101 next_event_ = find_next_event();
102 }
103 }
104 else
105 {
106 invalidate();
107 }
108 }
109
110
111 scalar timer::find_next_event()
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 scalar timer::next_expiration() const
132 {
133 return absolute_;
134 }
135
136 bool timer::is_expired() const
137 {
138 return seconds_remaining() < 0.0;
139 }
140
141 bool timer::is_repeating() const
142 {
143 return mode_ == repeat;
144 }
145
146
147 void timer::fire_expired_timers(scalar t)
148 {
149 if (t < next_event_) 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) * SCALAR(0.000000001);
195 }
196
197 void timer::sleep(scalar seconds, mode mode)
198 {
199 if (mode == absolute) seconds -= ticks();
200 if (seconds < SCALAR(0.0)) return;
201
202 struct timespec ts;
203 ts.tv_sec = seconds;
204 ts.tv_nsec = (seconds - scalar(ts.tv_sec)) * SCALAR(1000000000.0);
205
206 int ret;
207 do ret = nanosleep(&ts, &ts); while (ret == -1 && errno == EINTR);
208 }
209
210
211 #else // ! USE_CLOCK_GETTIME
212
213
214 // If we don't have posix timers, we'll have to use a different timing
215 // method. SDL only promises centisecond accuracy, but that's better than
216 // a kick in the pants. It could end up being just as good anyway.
217
218 scalar timer::ticks()
219 {
220 return scalar(SDL_GetTicks()) * SCALAR(0.001);
221 }
222
223 void timer::sleep(scalar seconds, mode mode)
224 {
225 if (mode == absolute) seconds -= ticks();
226 if (seconds < SCALAR(0.0)) return;
227 SDL_Delay(seconds * SCALAR(1000.0));
228 }
229
230 #endif // USE_CLOCK_GETTIME
231
232
233 } // namespace moof
234
This page took 0.041893 seconds and 4 git commands to generate.