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