]> Dogcows Code - chaz/yoink/blob - src/Moof/Timer.cc
tcp socket disconnecting by remote
[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 Mf {
27
28
29 Scalar Timer::gNextFire = std::numeric_limits<Scalar>::max();
30 std::map<unsigned,Timer*> Timer::gTimers;
31
32
33 unsigned Timer::getNewID()
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 mMode = mode;
45
46 if (mMode != INVALID)
47 {
48 mFunction = function;
49
50 if (mode == ACTUAL)
51 {
52 mAbsolute = seconds;
53 }
54 else
55 {
56 mAbsolute = seconds - getTicks();
57 mInterval = seconds;
58 }
59
60 mId = getNewID();
61 gTimers.insert(std::pair<unsigned,Timer*>(mId, this));
62
63 if (mAbsolute < gNextFire) gNextFire = mAbsolute;
64 }
65 }
66
67
68 bool Timer::isValid() const
69 {
70 return mMode != INVALID;
71 }
72
73 void Timer::invalidate()
74 {
75 if (mMode != INVALID)
76 {
77 gTimers.erase(mId);
78 mMode = INVALID;
79
80 if (isEqual(mAbsolute, gNextFire)) gNextFire = findNextFire();
81 }
82 }
83
84
85 void Timer::fire()
86 {
87 Scalar t = getTicks();
88
89 if (mFunction) mFunction(*this, t);
90
91 if (isRepeating())
92 {
93 Scalar absolute = mAbsolute;
94
95 if (isEqual(mAbsolute, t, 1.0)) mAbsolute += mInterval;
96 else mAbsolute = mInterval + t;
97
98 if (isEqual(absolute, gNextFire)) gNextFire = findNextFire();
99 }
100 else
101 {
102 invalidate();
103 }
104 }
105
106
107 Scalar Timer::findNextFire()
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->mAbsolute;
115 if (absolute < nextFire) nextFire = absolute;
116 }
117
118 return nextFire;
119 }
120
121
122 Scalar Timer::getSecondsRemaining() const
123 {
124 return mAbsolute - getTicks();
125 }
126
127 bool Timer::isExpired() const
128 {
129 return getSecondsRemaining() < 0.0;
130 }
131
132 bool Timer::isRepeating() const
133 {
134 return mMode == REPEAT;
135 }
136
137
138 void Timer::fireIfExpired()
139 {
140 fireIfExpired(getTicks());
141 }
142
143 void Timer::fireIfExpired(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->isExpired()) 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 setReference_()
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 = setReference_();
179
180
181 Scalar Timer::getTicks()
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 == ACTUAL) seconds -= getTicks();
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::getTicks()
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 == ACTUAL) seconds -= getTicks();
225 SDL_Delay(Uint32(cml::clamp(int(seconds * 1000.0), 0, 1000)));
226 }
227
228 #endif // HAVE_CLOCK_GETTIME
229
230
231 } // namespace Mf
232
This page took 0.041294 seconds and 4 git commands to generate.