1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 /* interface header */
14 #include "TimeKeeper.h"
15
16 /* system implementation headers */
17 #include <time.h>
18 #include <string>
19 #include <string.h>
20 #ifdef HAVE_UNISTD_H
21 # include <unistd.h>
22 #endif
23 #ifdef __BEOS__
24 # include <OS.h>
25 #endif
26 #if !defined(_WIN32)
27 # include <sys/time.h>
28 # include <sys/types.h>
29 static struct timeval lastTime = { 0, 0 };
30 #else /* !defined(_WIN32) */
31 # include <mmsystem.h>
32 static unsigned long int lastTime = 0;
33 static LARGE_INTEGER qpcLastTime;
34 static LONGLONG qpcFrequency = 0;
35 static LONGLONG qpcLastCalibration;
36 static DWORD timeLastCalibration;
37 #endif /* !defined(_WIN32) */
38
39 /* common implementation headers */
40 #include "TextUtils.h"
41 #include "bzfio.h"
42
43
44 TimeKeeper TimeKeeper::currentTime;
45 TimeKeeper TimeKeeper::tickTime;
46 TimeKeeper TimeKeeper::sunExplodeTime;
47 TimeKeeper TimeKeeper::sunGenesisTime;
48 TimeKeeper TimeKeeper::nullTime;
49 TimeKeeper TimeKeeper::startTime = TimeKeeper::getCurrent();
50
getCurrent(void)51 const TimeKeeper& TimeKeeper::getCurrent(void)
52 {
53 // if not first call then update current time, else use default initial time
54 #if !defined(_WIN32)
55 if (lastTime.tv_sec != 0)
56 {
57 struct timeval now;
58 gettimeofday(&now, NULL);
59 currentTime += double(now.tv_sec - lastTime.tv_sec) +
60 1.0e-6 * double(now.tv_usec - lastTime.tv_usec);
61 lastTime = now;
62 }
63 else
64 gettimeofday(&lastTime, NULL);
65 #else /* !defined(_WIN32) */
66 if (qpcFrequency != 0)
67 {
68
69 // main timer is qpc
70 LARGE_INTEGER now;
71 QueryPerformanceCounter(&now);
72
73 LONGLONG diff = now.QuadPart - qpcLastTime.QuadPart;
74 LONGLONG clkSpent = now.QuadPart - qpcLastCalibration;
75
76 if (clkSpent > qpcFrequency)
77 {
78 // Recalibrate Frequency
79 DWORD tgt = timeGetTime();
80 DWORD deltaTgt = tgt - timeLastCalibration;
81 timeLastCalibration = tgt;
82 qpcLastCalibration = now.QuadPart;
83 if (deltaTgt > 0)
84 {
85 LONGLONG oldqpcfreq = qpcFrequency;
86 qpcFrequency = (clkSpent * 1000) / deltaTgt;
87 if (qpcFrequency != oldqpcfreq)
88 logDebugMessage(4,"Recalibrated QPC frequency. Old: %f ; New: %f\n",
89 (double)oldqpcfreq, (double)qpcFrequency);
90 }
91 }
92
93 currentTime += (double) diff / (double) qpcFrequency;
94 qpcLastTime = now;
95 }
96 else if (lastTime != 0)
97 {
98 unsigned long int now = (unsigned long int)timeGetTime();
99 unsigned long int diff;
100 if (now <= lastTime)
101 {
102 // eh, how'd we go back in time?
103 diff = 0;
104 }
105 else
106 diff = now - lastTime;
107 currentTime += 1.0e-3 * (double)diff;
108 lastTime = now;
109 }
110 else
111 {
112 static bool sane = true;
113
114 // should only get into here once on app start
115 if (!sane)
116 logDebugMessage(1,"Sanity check failure in TimeKeeper::getCurrent()\n");
117 sane = false;
118
119 LARGE_INTEGER freq;
120 if (QueryPerformanceFrequency(&freq))
121 {
122 QueryPerformanceCounter(&qpcLastTime);
123 qpcFrequency = freq.QuadPart;
124 logDebugMessage(4,"Actual reported QPC Frequency: %f\n", (double)qpcFrequency);
125 qpcLastCalibration = qpcLastTime.QuadPart;
126 timeLastCalibration = timeGetTime();
127 }
128 else
129 {
130 logDebugMessage(1,"QueryPerformanceFrequency failed with error %d\n", GetLastError());
131
132 // make sure we're at our best timer resolution possible
133 timeBeginPeriod(1);
134
135 lastTime = (unsigned long int)timeGetTime();
136 }
137 }
138 #endif /* !defined(_WIN32) */
139 return currentTime;
140 }
141
getStartTime(void)142 const TimeKeeper& TimeKeeper::getStartTime(void) // const
143 {
144 return startTime;
145 }
146
getTick(void)147 const TimeKeeper& TimeKeeper::getTick(void) // const
148 {
149 return tickTime;
150 }
151
setTick(void)152 void TimeKeeper::setTick(void)
153 {
154 tickTime = getCurrent();
155 }
156
getSunExplodeTime(void)157 const TimeKeeper& TimeKeeper::getSunExplodeTime(void)
158 {
159 sunExplodeTime.seconds = 10000.0 * 365 * 24 * 60 * 60;
160 return sunExplodeTime;
161 }
162
getSunGenesisTime(void)163 const TimeKeeper& TimeKeeper::getSunGenesisTime(void)
164 {
165 sunGenesisTime.seconds = -10000.0 * 365 * 24 * 60 * 60;
166 return sunGenesisTime;
167 }
168
getNullTime(void)169 const TimeKeeper& TimeKeeper::getNullTime(void)
170 {
171 nullTime.seconds = 0;
172 return nullTime;
173 }
174
timestamp(void)175 const char *TimeKeeper::timestamp(void) // const
176 {
177 static char buffer[256]; // static, so that it doesn't vanish
178
179 int year, month, day, hour, min, sec;
180 TimeKeeper::localTime(&year, &month, &day, &hour, &min, &sec);
181
182 strncpy(buffer, TextUtils::format("%04d-%02d-%02d %02d:%02d:%02d",
183 year, month, day,
184 hour, min, sec).c_str(), 255);
185 buffer[255] = '\0'; // safety
186
187 return buffer;
188 }
189
localTime(int * year,int * month,int * day,int * hour,int * min,int * sec,bool * dst,long * tv_usec)190 void TimeKeeper::localTime(int *year, int *month, int* day, int* hour, int* min, int* sec, bool* dst,
191 long *tv_usec) // const
192 {
193 time_t tnow;
194 if (tv_usec)
195 {
196 #ifdef _WIN32
197 tnow = time(0);
198 *tv_usec = 0;
199 #else
200 struct timeval tv;
201 gettimeofday (&tv, NULL);
202 *tv_usec = tv.tv_usec;
203 tnow = tv.tv_sec;
204 #endif
205 }
206 else
207 tnow = time(0);
208 struct tm *now;
209 #ifdef _WIN32
210 now = localtime(&tnow);
211 #else
212 struct tm nowBuffer;
213 now = localtime_r(&tnow, &nowBuffer);
214 #endif
215 now->tm_year += 1900;
216 ++now->tm_mon;
217
218 if ( year )
219 *year = now->tm_year;
220 if ( month )
221 *month = now->tm_mon;
222 if ( day )
223 *day = now->tm_mday;
224 if ( hour )
225 *hour = now->tm_hour;
226 if ( min )
227 *min = now->tm_min;
228 if ( sec )
229 *sec = now->tm_sec;
230 if ( dst )
231 *dst = now->tm_isdst != 0;
232 }
233
234
UTCTime(int * year,int * month,int * day,int * wday,int * hour,int * min,int * sec,bool * dst,long * tv_usec)235 void TimeKeeper::UTCTime(int *year, int *month, int* day, int* wday,
236 int* hour, int* min, int* sec, bool* dst, long *tv_usec) // const
237 {
238 time_t tnow;
239 if (tv_usec)
240 {
241 #ifdef _WIN32
242 tnow = time(0);
243 *tv_usec = 0;
244 #else
245 struct timeval tv;
246 gettimeofday (&tv, NULL);
247 *tv_usec = tv.tv_usec;
248 tnow = tv.tv_sec;
249 #endif
250 }
251 else
252 tnow = time(0);
253 struct tm *now = gmtime(&tnow);
254 now->tm_year += 1900;
255 ++now->tm_mon;
256
257 if (year)
258 *year = now->tm_year;
259 if (month)
260 *month = now->tm_mon;
261 if (day)
262 *day = now->tm_mday;
263 if (wday)
264 *wday = now->tm_wday;
265 if (hour)
266 *hour = now->tm_hour;
267 if (min)
268 *min = now->tm_min;
269 if (sec)
270 *sec = now->tm_sec;
271 if (dst)
272 *dst = (now->tm_isdst != 0);
273 }
274
275 // function for converting a float time (e.g. difference of two TimeKeepers)
276 // into an array of ints
convertTime(double raw,long int convertedTimes[])277 void TimeKeeper::convertTime(double raw, long int convertedTimes[])
278 {
279 long int day, hour, min, sec, remainder;
280 static const int secondsInDay = 86400;
281
282 sec = (long int)raw;
283 day = sec / secondsInDay;
284 remainder = sec - (day * secondsInDay);
285 hour = remainder / 3600;
286 remainder = sec - ((hour * 3600) + (day * secondsInDay));
287 min = remainder / 60;
288 remainder = sec - ((hour * 3600) + (day * secondsInDay) + (min * 60));
289 sec = remainder;
290
291 convertedTimes[0] = day;
292 convertedTimes[1] = hour;
293 convertedTimes[2] = min;
294 convertedTimes[3] = sec;
295
296 return;
297 }
298
299 // function for printing an array of ints representing a time
300 // as a human-readable string
printTime(long int timeValue[])301 const std::string TimeKeeper::printTime(long int timeValue[])
302 {
303 std::string valueNames;
304 char temp[25];
305
306 if (timeValue[0] > 0)
307 {
308 snprintf(temp, 25, "%ld day%s", timeValue[0], timeValue[0] == 1 ? "" : "s");
309 valueNames.append(temp);
310 }
311 if (timeValue[1] > 0)
312 {
313 if (timeValue[0] > 0)
314 valueNames.append(", ");
315 snprintf(temp, 20, "%ld hour%s", timeValue[1], timeValue[1] == 1 ? "" : "s");
316 valueNames.append(temp);
317 }
318 if (timeValue[2] > 0)
319 {
320 if ((timeValue[1] > 0) || (timeValue[0] > 0))
321 valueNames.append(", ");
322 snprintf(temp, 20, "%ld min%s", timeValue[2], timeValue[2] == 1 ? "" : "s");
323 valueNames.append(temp);
324 }
325 if (timeValue[3] > 0)
326 {
327 if ((timeValue[2] > 0) || (timeValue[1] > 0) || (timeValue[0] > 0))
328 valueNames.append(", ");
329 snprintf(temp, 20, "%ld sec%s", timeValue[3], timeValue[3] == 1 ? "" : "s");
330 valueNames.append(temp);
331 }
332
333 return valueNames;
334 }
335
336 // function for printing a float time difference as a human-readable string
printTime(double diff)337 const std::string TimeKeeper::printTime(double diff)
338 {
339 long int temp[4];
340 convertTime(diff, temp);
341 return printTime(temp);
342 }
343
344
sleep(double seconds)345 void TimeKeeper::sleep(double seconds)
346 {
347 if (seconds <= 0.0)
348 return;
349
350 #ifdef HAVE_USLEEP
351 usleep((unsigned int)(1.0e6 * seconds));
352 return;
353 #endif
354 #if defined(HAVE_SLEEP) && !defined(__APPLE__)
355 // equivalent to _sleep() on win32 (not sleep(3))
356 Sleep((DWORD)(seconds * 1000.0));
357 return;
358 #endif
359 #ifdef HAVE_SNOOZE
360 snooze((bigtime_t)(1.0e6 * seconds));
361 return;
362 #endif
363 #ifdef HAVE_SELECT
364 struct timeval tv;
365 tv.tv_sec = (long)seconds;
366 tv.tv_usec = (long)(1.0e6 * (seconds - tv.tv_sec));
367 select(0, NULL, NULL, NULL, &tv);
368 return;
369 #endif
370 #ifdef HAVE_WAITFORSINGLEOBJECT
371 HANDLE dummyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
372 WaitForSingleObject(dummyEvent, (DWORD)(1000.0 * seconds));
373 CloseHandle(dummyEvent);
374 return;
375 #endif
376
377 // fall-back case is fugly manual timekeeping
378 TimeKeeper now = TimeKeeper::getCurrent();
379 while ((TimeKeeper::getCurrent() - now) < seconds)
380 continue;
381 return;
382 }
383
384
385 // Local Variables: ***
386 // mode: C++ ***
387 // tab-width: 4 ***
388 // c-basic-offset: 4 ***
389 // indent-tabs-mode: nil ***
390 // End: ***
391 // ex: shiftwidth=4 tabstop=4
392