1 /*
2  * C11 <time.h> implementation
3  *
4  * (C) Copyright yohhoy 2012.
5  * Copyright 2022 Yonggang Luo
6  * Distributed under the Boost Software License, Version 1.0.
7  *
8  * Permission is hereby granted, free of charge, to any person or organization
9  * obtaining a copy of the software and accompanying documentation covered by
10  * this license (the "Software") to use, reproduce, display, distribute,
11  * execute, and transmit the Software, and to prepare [[derivative work]]s of the
12  * Software, and to permit third-parties to whom the Software is furnished to
13  * do so, all subject to the following:
14  *
15  * The copyright notices in the Software and this entire statement, including
16  * the above license grant, this restriction and the following disclaimer,
17  * must be included in all copies of the Software, in whole or in part, and
18  * all derivative works of the Software, unless such copies or derivative
19  * works are solely in the form of machine-executable object code generated by
20  * a source language processor.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  */
30 
31 #include "c11/time.h"
32 
33 #ifdef _TIMESPEC_GET_NEED_IMPL
34 
35 #if defined(_WIN32) && !defined(HAVE_PTHREAD)
36 
37 #include "c11/threads.h"
38 #include <windows.h>
39 
40 static LARGE_INTEGER frequency;
41 
42 static
43 void
c23_timespec_get_init(void)44 c23_timespec_get_init(void)
45 {
46     QueryPerformanceFrequency(&frequency);
47 }
48 
49 int
c23_timespec_get(struct timespec * ts,int base)50 c23_timespec_get(struct timespec *ts, int base)
51 {
52 /* difference between 1970 and 1601 */
53 #define _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull
54 /* 1 tick is 100 nanoseconds */
55 #define _TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull
56     if (!ts)
57         return 0;
58     if (base == TIME_UTC) {
59         FILETIME ft;
60         ULARGE_INTEGER date;
61         LONGLONG ticks;
62 
63         GetSystemTimeAsFileTime(&ft);
64         date.HighPart = ft.dwHighDateTime;
65         date.LowPart = ft.dwLowDateTime;
66         ticks = (LONGLONG)(date.QuadPart - _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS);
67         ts->tv_sec = ticks / _TIMESPEC_IMPL_TICKS_PER_SECONDS;
68         ts->tv_nsec = (ticks % _TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100;
69         return base;
70     } else if (base == TIME_MONOTONIC || base == TIME_MONOTONIC_RAW) {
71         if (frequency.QuadPart == 0) {
72             static once_flag once = ONCE_FLAG_INIT;
73             call_once(&once, c23_timespec_get_init);
74         }
75         if (frequency.QuadPart != 0) {
76             LARGE_INTEGER now;
77             LONGLONG sec;
78             LONGLONG nsec;
79             QueryPerformanceCounter(&now);
80             sec = now.QuadPart / frequency.QuadPart;
81             nsec = (now.QuadPart - sec * frequency.QuadPart)
82                 * 1000000000UL / frequency.QuadPart;
83             ts->tv_sec = (time_t)sec;
84             ts->tv_nsec = (long)nsec;
85             return base;
86         }
87         /* Otherwise timespec_get with TIME_MONOTONIC or TIME_MONOTONIC_RAW failed */
88         return 0;
89     }
90     return 0;
91 #undef _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS
92 #undef _TIMESPEC_IMPL_TICKS_PER_SECONDS
93 }
94 
95 #else
96 
c23_timespec_get(struct timespec * ts,int base)97 int c23_timespec_get(struct timespec *ts, int base)
98 {
99     if (!ts)
100         return 0;
101     switch (base)
102     {
103     case TIME_UTC:
104         if (clock_gettime(CLOCK_REALTIME, ts) == 0)
105             return base;
106         break;
107 #ifdef CLOCK_MONOTONIC
108     case TIME_MONOTONIC:
109         if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
110             return base;
111         break;
112 #endif
113 #ifdef CLOCK_PROCESS_CPUTIME_ID
114     case TIME_ACTIVE:
115         if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts) == 0)
116             return base;
117         break;
118 #endif
119 #ifdef CLOCK_THREAD_CPUTIME_ID
120     case TIME_THREAD_ACTIVE:
121         if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts) == 0)
122             return base;
123         break;
124 #endif
125 #ifdef CLOCK_MONOTONIC_RAW
126     case TIME_MONOTONIC_RAW:
127         if (clock_gettime(CLOCK_MONOTONIC_RAW, ts) == 0)
128             return base;
129         break;
130 #endif
131     default:
132         break;
133     }
134     return 0;
135 }
136 #endif
137 
138 #endif /* !HAVE_TIMESPEC_GET */
139