]> git.mxchange.org Git - simgear.git/blob - simgear/timing/timestamp.cxx
Trying to make old gcc on Jenkins happy.
[simgear.git] / simgear / timing / timestamp.cxx
1 /**
2  * \file timestamp.cxx
3  * Provides a class for managing a timestamp (seconds & milliseconds.)
4  */
5
6 // Written by Curtis Olson, started December 1998.
7 //
8 // Copyright (C) 1998  Curtis L. Olson  - http://www.flightgear.org/~curt
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 // General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 //
24 // $Id$
25
26
27 #ifdef HAVE_CONFIG_H
28 #  include <simgear_config.h>
29 #endif
30
31 #include <simgear/compiler.h>
32
33 #include <ctime>
34 #include <cerrno>
35
36 #ifdef HAVE_SYS_TIMEB_H
37 #  include <sys/timeb.h> // for ftime() and struct timeb
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #  include <unistd.h>    // for gettimeofday() and the _POSIX_TIMERS define
41 #endif
42 #ifdef HAVE_SYS_TIME_H
43 #  include <sys/time.h>  // for get/setitimer, gettimeofday, struct timeval
44 #endif
45
46 #if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS)
47 #  include <time.h>
48 #endif
49
50 #ifdef WIN32
51 #  include <windows.h>
52 #  if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
53 #    define NEAR /* */
54 #    define FAR  /* */
55 #  endif
56 #  include <mmsystem.h>
57 #endif
58
59 #include "timestamp.hxx"
60
61 #if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS)
62 static clockid_t getClockId()
63 {
64 #if defined(_POSIX_MONOTONIC_CLOCK)
65     static clockid_t clockid = CLOCK_MONOTONIC;
66     static bool firstTime = true;
67     if (!firstTime)
68         return clockid;
69
70     firstTime = false;
71     // For the first time test if the monotonic clock is available.
72     // If so use this, if not use the realtime clock.
73     struct timespec ts;
74     if (-1 == clock_gettime(clockid, &ts) && errno == EINVAL)
75         clockid = CLOCK_REALTIME;
76     return clockid;
77 #else
78     return CLOCK_REALTIME;
79 #endif
80 }
81
82 #endif
83
84 void SGTimeStamp::stamp() {
85 #ifdef _WIN32
86     unsigned int t;
87     t = timeGetTime();
88     _sec = t / 1000;
89     _nsec = ( t - ( _sec * 1000 ) ) * 1000 * 1000;
90 #elif defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS)
91     struct timespec ts;
92     clock_gettime(getClockId(), &ts);
93     _sec = ts.tv_sec;
94     _nsec = ts.tv_nsec;
95 #elif defined( HAVE_GETTIMEOFDAY )
96     struct timeval current;
97     gettimeofday(&current, NULL);
98     _sec = current.tv_sec;
99     _nsec = current.tv_usec * 1000;
100 #elif defined( HAVE_GETLOCALTIME )
101     SYSTEMTIME current;
102     GetLocalTime(&current);
103     _sec = current.wSecond;
104     _nsec = current.wMilliseconds * 1000 * 1000;
105 #elif defined( HAVE_FTIME )
106     struct timeb current;
107     ftime(&current);
108     _sec = current.time;
109     _nsec = current.millitm * 1000 * 1000;
110 #else
111 # error Port me
112 #endif
113 }
114
115 // sleep based timing loop.
116 //
117 // Calling sleep, even usleep() on linux is less accurate than
118 // we like, but it does free up the cpu for other tasks during
119 // the sleep so it is desirable.  Because of the way sleep()
120 // is implemented in consumer operating systems like windows
121 // and linux, you almost always sleep a little longer than the
122 // requested amount.
123 //
124 // To combat the problem of sleeping too long, we calculate the
125 // desired wait time and shorten it by 2000us (2ms) to avoid
126 // [hopefully] over-sleep'ing.  The 2ms value was arrived at
127 // via experimentation.  We follow this up at the end with a
128 // simple busy-wait loop to get the final pause timing exactly
129 // right.
130 //
131 // Assuming we don't oversleep by more than 2000us, this
132 // should be a reasonable compromise between sleep based
133 // waiting, and busy waiting.
134 //
135 // Usually posix timer resolutions are low enough that we
136 // could just leave this to the operating system today.
137 // The day where the busy loop was introduced in flightgear,
138 // the usual kernels still had just about 10ms (=HZ for
139 // the timer tick) accuracy which is too bad to catch 60Hz...
140 bool SGTimeStamp::sleepUntil(const SGTimeStamp& abstime)
141 {
142     // FreeBSD is missing clock_nanosleep, see
143     // https://wiki.freebsd.org/FreeBSD_and_Standards
144 #if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) && !defined(__FreeBSD__)
145     SGTimeStamp abstimeForSleep = abstime;
146
147     // Always undersleep by resolution of the clock
148     struct timespec ts;
149     if (-1 != clock_getres(getClockId(), &ts)) {
150         abstimeForSleep -= SGTimeStamp::fromSecNSec(ts.tv_sec, ts.tv_nsec);
151     } else {
152         abstimeForSleep -= SGTimeStamp::fromSecMSec(0, 2);
153     }
154
155     ts.tv_sec = abstimeForSleep._sec;
156     ts.tv_nsec = abstimeForSleep._nsec;
157     for (;;) {
158         int ret = clock_nanosleep(getClockId(), TIMER_ABSTIME, &ts, NULL);
159         if (-1 == ret && errno != EINTR)
160             return false;
161         if (ret == 0)
162             break;
163     }
164
165     // The busy loop for the rest
166     SGTimeStamp currentTime;
167     do {
168         currentTime.stamp();
169     } while (currentTime < abstime);
170
171     return true;
172
173 #elif defined _WIN32
174
175     SGTimeStamp currentTime;
176     currentTime.stamp();
177     if (abstime <= currentTime)
178         return true;
179
180     SGTimeStamp abstimeForSleep = abstime - SGTimeStamp::fromSecMSec(0, 2);
181     for (;abstimeForSleep < currentTime;) {
182         SGTimeStamp timeDiff = abstimeForSleep - currentTime;
183         if (timeDiff < SGTimeStamp::fromSecMSec(0, 1))
184             break;
185         // Don't know, but may be win32 has something better today??
186         Sleep(static_cast<DWORD>(timeDiff.toMSecs()));
187
188         currentTime.stamp();
189     }
190
191     // Follow by a busy loop
192     while (currentTime < abstime) {
193         currentTime.stamp();
194     }
195
196     return true;
197
198 #else
199
200     SGTimeStamp currentTime;
201     currentTime.stamp();
202     if (abstime <= currentTime)
203         return true;
204
205     SGTimeStamp abstimeForSleep = abstime - SGTimeStamp::fromSecMSec(0, 2);
206     for (;abstimeForSleep < currentTime;) {
207         SGTimeStamp timeDiff = abstimeForSleep - currentTime;
208         if (timeDiff < SGTimeStamp::fromSecUSec(0, 1))
209             break;
210         // Its documented that some systems bail out on usleep for longer than 1s
211         // since we recheck the current time anyway just wait for
212         // less than a second multiple times
213         bool truncated = false;
214         if (SGTimeStamp::fromSec(1) < timeDiff) {
215             timeDiff = SGTimeStamp::fromSec(1);
216             truncated = true;
217         }
218         int ret = usleep(static_cast<int>(timeDiff.toUSecs()));
219         if (-1 == ret && errno != EINTR)
220             return false;
221         if (ret == 0 && !truncated)
222             break;
223
224         currentTime.stamp();
225     }
226
227     // Follow by a busy loop
228     while (currentTime < abstime) {
229         currentTime.stamp();
230     }
231
232     return true;
233
234 #endif
235 }
236
237 bool SGTimeStamp::sleepFor(const SGTimeStamp& reltime)
238 {
239     // see comment above regarding FreeBSD
240 #if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) && !defined(__FreeBSD__)
241     struct timespec ts;
242     ts.tv_sec = reltime._sec;
243     ts.tv_nsec = reltime._nsec;
244     for (;;) {
245         struct timespec rem;
246         int ret = clock_nanosleep(getClockId(), 0, &ts, &rem);
247         if (-1 == ret && errno != EINTR)
248             return false;
249         if (ret == 0)
250             break;
251         // Use the remainder for the next cycle.
252         ts = rem;
253     }
254     return true;
255 #elif defined _WIN32
256     if (reltime < SGTimeStamp::fromSecMSec(0, 1))
257         return true;
258     // Don't know, but may be win32 has something better today??
259     Sleep(static_cast<DWORD>(reltime.toMSecs()));
260     return true;
261 #elif defined __APPLE__
262     // the generic version below behaves badly on Mac; use nanosleep directly,
263     // similar to the POSIX timers version
264     struct timespec ts;
265     ts.tv_sec = reltime._sec;
266     ts.tv_nsec = reltime._nsec;
267     
268     for (;;) {
269         struct timespec rem;
270         int ret = nanosleep(&ts, &rem);
271         if (-1 == ret && errno != EINTR)
272             return false;
273         if (ret == 0)
274             break;
275         // Use the remainder for the next cycle.
276         ts = rem;
277     }
278     return true;
279 #else
280     SGTimeStamp abstime;
281     abstime.stamp();
282     abstime += reltime;
283
284     SGTimeStamp currentTime;
285     currentTime.stamp();
286     for (;abstime < currentTime;) {
287         SGTimeStamp timeDiff = abstime - currentTime;
288         if (timeDiff < SGTimeStamp::fromSecUSec(0, 1))
289             break;
290         // Its documented that some systems bail out on usleep for longer than 1s
291         // since we recheck the current time anyway just wait for
292         // less than a second multiple times
293         bool truncated = false;
294         if (SGTimeStamp::fromSec(1) < timeDiff) {
295             timeDiff = SGTimeStamp::fromSec(1);
296             truncated = true;
297         }
298         int ret = usleep(static_cast<int>(timeDiff.toUSecs()));
299         if (-1 == ret && errno != EINTR)
300             return false;
301         if (ret == 0 && !truncated)
302             break;
303
304         currentTime.stamp();
305     }
306
307     return true;
308 #endif
309 }
310
311 int SGTimeStamp::elapsedMSec() const
312 {
313     SGTimeStamp now;
314     now.stamp();
315     
316     return static_cast<int>((now - *this).toMSecs());
317 }