Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wtime.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * emulate POSIX time functionality on Windows.
25  */
26 
27 // note: clock_gettime et al. have been removed. callers should use the
28 // WHRT directly, rather than needlessly translating s -> ns -> s,
29 // which costs time and accuracy.
30 
31 #include "precompiled.h"
33 
36 
37 WINIT_REGISTER_MAIN_INIT(wtime_Init); // whrt -> wtime
38 
39 // NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC.
40 // SYSTEMTIME is a struct containing month, year, etc.
41 
42 static const long _1e3 = 1000;
43 static const long _1e6 = 1000000;
44 static const long _1e7 = 10000000;
45 static const i64 _1e9 = 1000000000;
46 
47 
48 //
49 // FILETIME -> time_t routines; used by wposix filetime_to_time_t wrapper.
50 //
51 
52 // hectonanoseconds between Windows and POSIX epoch
53 static const u64 posix_epoch_hns = 0x019DB1DED53E8000;
54 
55 // this function avoids the pitfall of casting FILETIME* to u64*,
56 // which is not safe due to differing alignment guarantees!
57 // on some platforms, that would result in an exception.
58 static u64 u64_from_FILETIME(const FILETIME* ft)
59 {
60  return u64_from_u32(ft->dwHighDateTime, ft->dwLowDateTime);
61 }
62 
63 
64 // convert UTC FILETIME to seconds-since-1970 UTC:
65 // we just have to subtract POSIX epoch and scale down to units of seconds.
66 //
67 // used by wfilesystem.
68 //
69 // note: RtlTimeToSecondsSince1970 isn't officially documented,
70 // so don't use that.
71 time_t wtime_utc_filetime_to_time_t(FILETIME* ft)
72 {
73  u64 hns = u64_from_FILETIME(ft);
74  u64 s = (hns - posix_epoch_hns) / _1e7;
75  return (time_t)(s & 0xFFFFFFFF);
76 }
77 
78 
79 //-----------------------------------------------------------------------------
80 
81 // system clock at startup [nanoseconds since POSIX epoch]
82 // note: the HRT starts at 0; any increase by the time we get here
83 // just makes our notion of the start time more accurate)
85 
87 {
88  FILETIME ft;
89  GetSystemTimeAsFileTime(&ft);
90  const u64 hns = u64_from_FILETIME(&ft);
91  stInitial_ns = (hns - posix_epoch_hns) * 100;
92 }
93 
94 // return nanoseconds since POSIX epoch.
95 // algorithm: add current HRT value to the startup system time
97 {
98  const i64 ns = stInitial_ns + i64(whrt_Time() * _1e9);
99  return ns;
100 }
101 
103 {
104  timespec ts;
105  ts.tv_sec = (time_t)((ns / _1e9) & 0xFFFFFFFF);
106  ts.tv_nsec = (long)(ns % _1e9);
107  return ts;
108 }
109 
110 static size_t MsFromTimespec(const timespec& ts)
111 {
112  i64 ms = ts.tv_sec; // avoid overflow
113  ms *= _1e3;
114  ms += ts.tv_nsec / _1e6;
115  return size_t(ms);
116 }
117 
118 
119 //-----------------------------------------------------------------------------
120 
121 int clock_gettime(clockid_t clock, struct timespec* ts)
122 {
123  ENSURE(clock == CLOCK_REALTIME || clock == CLOCK_MONOTONIC);
124 
125  const i64 ns = CurrentSystemTime_ns();
126  *ts = TimespecFromNs(ns);
127  return 0;
128 }
129 
130 
131 int clock_getres(clockid_t clock, struct timespec* ts)
132 {
133  ENSURE(clock == CLOCK_REALTIME || clock == CLOCK_MONOTONIC);
134 
135  const double resolution = whrt_Resolution();
136  const i64 ns = i64(resolution * 1e9);
137  *ts = TimespecFromNs(ns);
138  return 0;
139 }
140 
141 
142 int nanosleep(const struct timespec* rqtp, struct timespec* /* rmtp */)
143 {
144  const DWORD ms = (DWORD)MsFromTimespec(*rqtp);
145  if(ms)
146  Sleep(ms);
147  return 0;
148 }
149 
150 
151 unsigned sleep(unsigned sec)
152 {
153  // warn if overflow would result (it would be insane to ask for
154  // such lengthy sleep timeouts, but still)
155  ENSURE(sec < std::numeric_limits<size_t>::max()/1000);
156 
157  const DWORD ms = sec * 1000;
158  if(ms)
159  Sleep(ms);
160  return sec;
161 }
162 
163 
165 {
166  ENSURE(us < _1e6);
167 
168  const DWORD ms = us/1000;
169  if(ms)
170  Sleep(ms);
171  return 0;
172 }
173 
174 
175 //-----------------------------------------------------------------------------
176 // strptime from NetBSD
177 
178 /*
179 * Copyright (c) 1999 Kungliga Tekniska Högskolan
180 * (Royal Institute of Technology, Stockholm, Sweden).
181 * All rights reserved.
182 *
183 * Redistribution and use in source and binary forms, with or without
184 * modification, are permitted provided that the following conditions
185 * are met:
186 *
187 * 1. Redistributions of source code must retain the above copyright
188 * notice, this list of conditions and the following disclaimer.
189 *
190 * 2. Redistributions in binary form must reproduce the above copyright
191 * notice, this list of conditions and the following disclaimer in the
192 * documentation and/or other materials provided with the distribution.
193 *
194 * 3. Neither the name of KTH nor the names of its contributors may be
195 * used to endorse or promote products derived from this software without
196 * specific prior written permission.
197 *
198 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
199 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
200 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
201 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
202 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
203 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
204 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
205 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
206 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
207 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
208 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
209 
210 static const char *abb_weekdays[] = {
211  "Sun",
212  "Mon",
213  "Tue",
214  "Wed",
215  "Thu",
216  "Fri",
217  "Sat",
218  NULL
219 };
220 
221 static const char *full_weekdays[] = {
222  "Sunday",
223  "Monday",
224  "Tuesday",
225  "Wednesday",
226  "Thursday",
227  "Friday",
228  "Saturday",
229  NULL
230 };
231 
232 static const char *abb_month[] = {
233  "Jan",
234  "Feb",
235  "Mar",
236  "Apr",
237  "May",
238  "Jun",
239  "Jul",
240  "Aug",
241  "Sep",
242  "Oct",
243  "Nov",
244  "Dec",
245  NULL
246 };
247 
248 static const char *full_month[] = {
249  "January",
250  "February",
251  "Mars",
252  "April",
253  "May",
254  "June",
255  "July",
256  "August",
257  "September",
258  "October",
259  "November",
260  "December",
261  NULL,
262 };
263 
264 static const char *ampm[] = {
265  "am",
266  "pm",
267  NULL
268 };
269 
270 /*
271 * Try to match `*buf' to one of the strings in `strs'. Return the
272 * index of the matching string (or -1 if none). Also advance buf.
273 */
274 
275 static int
276 match_string (const char **buf, const char **strs)
277 {
278  int i = 0;
279 
280  for (i = 0; strs[i] != NULL; ++i) {
281  size_t len = strlen (strs[i]);
282 
283  if (strncasecmp (*buf, strs[i], len) == 0) {
284  *buf += len;
285  return i;
286  }
287  }
288  return -1;
289 }
290 
291 /*
292 * tm_year is relative this year */
293 
294 const int tm_year_base = 1900;
295 
296 /*
297 * Return TRUE iff `year' was a leap year.
298 */
299 
300 static int
301 is_leap_year (int year)
302 {
303  return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
304 }
305 
306 /*
307 * Return the weekday [0,6] (0 = Sunday) of the first day of `year'
308 */
309 
310 static int
311 first_day (int year)
312 {
313  int ret = 4;
314 
315  for (; year > 1970; --year)
316  ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7;
317  return ret;
318 }
319 
320 /*
321 * Set `timeptr' given `wnum' (week number [0, 53])
322 */
323 
324 static void
325 set_week_number_sun (struct tm *timeptr, int wnum)
326 {
327  int fday = first_day (timeptr->tm_year + tm_year_base);
328 
329  timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday;
330  if (timeptr->tm_yday < 0) {
331  timeptr->tm_wday = fday;
332  timeptr->tm_yday = 0;
333  }
334 }
335 
336 /*
337 * Set `timeptr' given `wnum' (week number [0, 53])
338 */
339 
340 static void
341 set_week_number_mon (struct tm *timeptr, int wnum)
342 {
343  int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
344 
345  timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday;
346  if (timeptr->tm_yday < 0) {
347  timeptr->tm_wday = (fday + 1) % 7;
348  timeptr->tm_yday = 0;
349  }
350 }
351 
352 /*
353 * Set `timeptr' given `wnum' (week number [0, 53])
354 */
355 
356 static void
357 set_week_number_mon4 (struct tm *timeptr, int wnum)
358 {
359  int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7;
360  int offset = 0;
361 
362  if (fday < 4)
363  offset += 7;
364 
365  timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday;
366  if (timeptr->tm_yday < 0) {
367  timeptr->tm_wday = fday;
368  timeptr->tm_yday = 0;
369  }
370 }
371 
372 /*
373 *
374 */
375 
376 char *
377 strptime (const char *buf, const char *format, struct tm *timeptr)
378 {
379  char c;
380 
381  for (; (c = *format) != '\0'; ++format) {
382  char *s;
383  int ret;
384 
385  if (isspace (c)) {
386  while (isspace (*buf))
387  ++buf;
388  } else if (c == '%' && format[1] != '\0') {
389  c = *++format;
390  if (c == 'E' || c == 'O')
391  c = *++format;
392  switch (c) {
393  case 'A' :
394  ret = match_string (&buf, full_weekdays);
395  if (ret < 0)
396  return NULL;
397  timeptr->tm_wday = ret;
398  break;
399  case 'a' :
400  ret = match_string (&buf, abb_weekdays);
401  if (ret < 0)
402  return NULL;
403  timeptr->tm_wday = ret;
404  break;
405  case 'B' :
406  ret = match_string (&buf, full_month);
407  if (ret < 0)
408  return NULL;
409  timeptr->tm_mon = ret;
410  break;
411  case 'b' :
412  case 'h' :
413  ret = match_string (&buf, abb_month);
414  if (ret < 0)
415  return NULL;
416  timeptr->tm_mon = ret;
417  break;
418  case 'C' :
419  ret = strtol (buf, &s, 10);
420  if (s == buf)
421  return NULL;
422  timeptr->tm_year = (ret * 100) - tm_year_base;
423  buf = s;
424  break;
425  case 'c' :
426  abort ();
427  case 'D' : /* %m/%d/%y */
428  s = strptime (buf, "%m/%d/%y", timeptr);
429  if (s == NULL)
430  return NULL;
431  buf = s;
432  break;
433  case 'd' :
434  case 'e' :
435  ret = strtol (buf, &s, 10);
436  if (s == buf)
437  return NULL;
438  timeptr->tm_mday = ret;
439  buf = s;
440  break;
441  case 'H' :
442  case 'k' :
443  ret = strtol (buf, &s, 10);
444  if (s == buf)
445  return NULL;
446  timeptr->tm_hour = ret;
447  buf = s;
448  break;
449  case 'I' :
450  case 'l' :
451  ret = strtol (buf, &s, 10);
452  if (s == buf)
453  return NULL;
454  if (ret == 12)
455  timeptr->tm_hour = 0;
456  else
457  timeptr->tm_hour = ret;
458  buf = s;
459  break;
460  case 'j' :
461  ret = strtol (buf, &s, 10);
462  if (s == buf)
463  return NULL;
464  timeptr->tm_yday = ret - 1;
465  buf = s;
466  break;
467  case 'm' :
468  ret = strtol (buf, &s, 10);
469  if (s == buf)
470  return NULL;
471  timeptr->tm_mon = ret - 1;
472  buf = s;
473  break;
474  case 'M' :
475  ret = strtol (buf, &s, 10);
476  if (s == buf)
477  return NULL;
478  timeptr->tm_min = ret;
479  buf = s;
480  break;
481  case 'n' :
482  if (*buf == '\n')
483  ++buf;
484  else
485  return NULL;
486  break;
487  case 'p' :
488  ret = match_string (&buf, ampm);
489  if (ret < 0)
490  return NULL;
491  if (timeptr->tm_hour == 0) {
492  if (ret == 1)
493  timeptr->tm_hour = 12;
494  } else
495  timeptr->tm_hour += 12;
496  break;
497  case 'r' : /* %I:%M:%S %p */
498  s = strptime (buf, "%I:%M:%S %p", timeptr);
499  if (s == NULL)
500  return NULL;
501  buf = s;
502  break;
503  case 'R' : /* %H:%M */
504  s = strptime (buf, "%H:%M", timeptr);
505  if (s == NULL)
506  return NULL;
507  buf = s;
508  break;
509  case 'S' :
510  ret = strtol (buf, &s, 10);
511  if (s == buf)
512  return NULL;
513  timeptr->tm_sec = ret;
514  buf = s;
515  break;
516  case 't' :
517  if (*buf == '\t')
518  ++buf;
519  else
520  return NULL;
521  break;
522  case 'T' : /* %H:%M:%S */
523  case 'X' :
524  s = strptime (buf, "%H:%M:%S", timeptr);
525  if (s == NULL)
526  return NULL;
527  buf = s;
528  break;
529  case 'u' :
530  ret = strtol (buf, &s, 10);
531  if (s == buf)
532  return NULL;
533  timeptr->tm_wday = ret - 1;
534  buf = s;
535  break;
536  case 'w' :
537  ret = strtol (buf, &s, 10);
538  if (s == buf)
539  return NULL;
540  timeptr->tm_wday = ret;
541  buf = s;
542  break;
543  case 'U' :
544  ret = strtol (buf, &s, 10);
545  if (s == buf)
546  return NULL;
547  set_week_number_sun (timeptr, ret);
548  buf = s;
549  break;
550  case 'V' :
551  ret = strtol (buf, &s, 10);
552  if (s == buf)
553  return NULL;
554  set_week_number_mon4 (timeptr, ret);
555  buf = s;
556  break;
557  case 'W' :
558  ret = strtol (buf, &s, 10);
559  if (s == buf)
560  return NULL;
561  set_week_number_mon (timeptr, ret);
562  buf = s;
563  break;
564  case 'x' :
565  s = strptime (buf, "%Y:%m:%d", timeptr);
566  if (s == NULL)
567  return NULL;
568  buf = s;
569  break;
570  case 'y' :
571  ret = strtol (buf, &s, 10);
572  if (s == buf)
573  return NULL;
574  if (ret < 70)
575  timeptr->tm_year = 100 + ret;
576  else
577  timeptr->tm_year = ret;
578  buf = s;
579  break;
580  case 'Y' :
581  ret = strtol (buf, &s, 10);
582  if (s == buf)
583  return NULL;
584  timeptr->tm_year = ret - tm_year_base;
585  buf = s;
586  break;
587  case 'Z' :
588  abort ();
589  case '\0' :
590  --format;
591  /* FALLTHROUGH */
592  case '%' :
593  if (*buf == '%')
594  ++buf;
595  else
596  return NULL;
597  break;
598  default :
599  if (*buf == '%' || *++buf == c)
600  ++buf;
601  else
602  return NULL;
603  break;
604  }
605  } else {
606  if (*buf == c)
607  ++buf;
608  else
609  return NULL;
610  }
611  }
612  return (char *)buf;
613 }
614 
615 
616 //-----------------------------------------------------------------------------
617 
619 {
621  return INFO::OK;
622 }
clockid_t
Definition: wtime.h:51
static void set_week_number_mon4(struct tm *timeptr, int wnum)
Definition: wtime.cpp:357
unsigned sleep(unsigned sec)
Definition: wtime.cpp:151
const Status OK
Definition: status.h:386
static void LatchInitialSystemTime()
Definition: wtime.cpp:86
static size_t MsFromTimespec(const timespec &ts)
Definition: wtime.cpp:110
static int match_string(const char **buf, const char **strs)
Definition: wtime.cpp:276
int clock_gettime(clockid_t clock, struct timespec *ts)
Definition: wtime.cpp:121
static const char * abb_month[]
Definition: wtime.cpp:232
static const i64 _1e9
Definition: wtime.cpp:45
static const u64 posix_epoch_hns
Definition: wtime.cpp:53
static i64 CurrentSystemTime_ns()
Definition: wtime.cpp:96
static void set_week_number_sun(struct tm *timeptr, int wnum)
Definition: wtime.cpp:325
time_t tv_sec
Definition: wtime.h:64
char * strptime(const char *buf, const char *format, struct tm *timeptr)
Definition: wtime.cpp:377
int usleep(useconds_t us)
Definition: wtime.cpp:164
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
static int first_day(int year)
Definition: wtime.cpp:311
long tv_nsec
Definition: wtime.h:65
static timespec TimespecFromNs(i64 ns)
Definition: wtime.cpp:102
double whrt_Resolution()
Definition: whrt.cpp:143
unsigned long DWORD
Definition: wgl.h:56
static const char * abb_weekdays[]
Definition: wtime.cpp:210
static const long _1e7
Definition: wtime.cpp:44
static const char * full_weekdays[]
Definition: wtime.cpp:221
i64 Status
Error handling system.
Definition: status.h:171
static void set_week_number_mon(struct tm *timeptr, int wnum)
Definition: wtime.cpp:341
static const char * ampm[]
Definition: wtime.cpp:264
double whrt_Time()
Definition: whrt.cpp:208
#define u64
Definition: types.h:42
#define WINIT_REGISTER_MAIN_INIT(func)
Definition: winit.h:148
unsigned long useconds_t
Definition: wtime.h:35
static int is_leap_year(int year)
Definition: wtime.cpp:301
#define i64
Definition: types.h:37
const int tm_year_base
Definition: wtime.cpp:294
static Status wtime_Init()
Definition: wtime.cpp:618
static double resolution
Definition: whrt.cpp:99
int clock_getres(clockid_t clock, struct timespec *ts)
Definition: wtime.cpp:131
u64 u64_from_u32(u32 hi, u32 lo)
return lower 16-bits
Definition: lib.cpp:65
time_t wtime_utc_filetime_to_time_t(FILETIME *ft)
Definition: wtime.cpp:71
static const char * full_month[]
Definition: wtime.cpp:248
Definition: wtime.h:62
static const long _1e3
Definition: wtime.cpp:42
int nanosleep(const struct timespec *rqtp, struct timespec *)
Definition: wtime.cpp:142
static i64 stInitial_ns
Definition: wtime.cpp:84
static volatile TimerState *volatile ts
Definition: whrt.cpp:187
static const long _1e6
Definition: wtime.cpp:43
static u64 u64_from_FILETIME(const FILETIME *ft)
Definition: wtime.cpp:58