Lely core libraries 2.3.4
clock.c
Go to the documentation of this file.
1
23#include "libc.h"
24
25#if !LELY_NO_RT
26
27#if _WIN32 && !defined(__MINGW32__)
28
29#include <lely/libc/time.h>
30
31#include <errno.h>
32
33#include <windows.h>
34
40#define FILETIME_EPOCH ((LONGLONG)(369 * 365 + 89) * 24 * 60 * 60)
41
42#ifdef _USE_32BIT_TIME_T
44#define TIME_T_MIN LONG_MIN
46#define TIME_T_MAX LONG_MAX
47#else
49#define TIME_T_MIN _I64_MIN
51#define TIME_T_MAX _I64_MAX
52#endif
53
54int
55clock_getres(clockid_t clock_id, struct timespec *res)
56{
57 if (clock_id == CLOCK_MONOTONIC) {
58 // On Windows XP or later, this will always succeed.
59 LARGE_INTEGER liFrequency;
60 QueryPerformanceFrequency(&liFrequency);
61 LONGLONG pf = liFrequency.QuadPart;
62
63 if (res)
64 *res = (struct timespec){ 0,
65 (long)((1000000000l + pf / 2) / pf) };
66
67 return 0;
68 }
69
70 switch (clock_id) {
71 case CLOCK_REALTIME:
72 case CLOCK_PROCESS_CPUTIME_ID:
73 case CLOCK_THREAD_CPUTIME_ID: break;
74 default: errno = EINVAL; return -1;
75 }
76
77 DWORD dwTimeAdjustment;
78 DWORD dwTimeIncrement;
79 BOOL bTimeAdjustmentDisabled;
80 // clang-format off
81 if (!GetSystemTimeAdjustment(&dwTimeAdjustment, &dwTimeIncrement,
82 &bTimeAdjustmentDisabled))
83 // clang-format on
84 return -1;
85
86 if (res)
87 *res = (struct timespec){ 0, dwTimeIncrement * 100 };
88
89 return 0;
90}
91
92int
93clock_gettime(clockid_t clock_id, struct timespec *tp)
94{
95 if (clock_id == CLOCK_MONOTONIC) {
96 // On Windows XP or later, this will always succeed.
97 LARGE_INTEGER liFrequency;
98 QueryPerformanceFrequency(&liFrequency);
99 LONGLONG pf = liFrequency.QuadPart;
100 LARGE_INTEGER liPerformanceCount;
101 QueryPerformanceCounter(&liPerformanceCount);
102 LONGLONG pc = liPerformanceCount.QuadPart;
103 if (pc / pf > TIME_T_MAX) {
104 errno = EOVERFLOW;
105 return -1;
106 }
107
108 if (tp) {
109 tp->tv_sec = pc / pf;
110 tp->tv_nsec = (long)(((pc % pf) * (1000000000l + pf / 2))
111 / pf);
112 }
113
114 return 0;
115 }
116
117 LONGLONG ft;
118 switch (clock_id) {
119 case CLOCK_REALTIME: {
120 FILETIME SystemTimeAsFileTime;
121#if defined(NTDDI_WIN8) && NTDDI_VERSION >= NTDDI_WIN8
122 GetSystemTimePreciseAsFileTime(&SystemTimeAsFileTime);
123#else
124 GetSystemTimeAsFileTime(&SystemTimeAsFileTime);
125#endif
126 ULARGE_INTEGER st = {
127 .LowPart = SystemTimeAsFileTime.dwLowDateTime,
128 .HighPart = SystemTimeAsFileTime.dwHighDateTime
129 };
130 ft = st.QuadPart - (ULONGLONG)FILETIME_EPOCH * 10000000ul;
131 break;
132 }
133 case CLOCK_PROCESS_CPUTIME_ID: {
134 FILETIME CreationTime, ExitTime, KernelTime, UserTime;
135 // clang-format off
136 if (!GetProcessTimes(GetCurrentProcess(), &CreationTime,
137 &ExitTime, &KernelTime, &UserTime))
138 // clang-format on
139 return -1;
140 // Add the time spent in kernel mode and the time spent in user
141 // mode to obtain the total process time.
142 ULARGE_INTEGER kt = { .LowPart = KernelTime.dwLowDateTime,
143 .HighPart = KernelTime.dwHighDateTime };
144 ULARGE_INTEGER ut = { .LowPart = UserTime.dwLowDateTime,
145 .HighPart = UserTime.dwHighDateTime };
146 if (kt.QuadPart + ut.QuadPart > _I64_MAX) {
147 errno = EOVERFLOW;
148 return -1;
149 }
150 ft = kt.QuadPart + ut.QuadPart;
151 break;
152 }
153 case CLOCK_THREAD_CPUTIME_ID: {
154 FILETIME CreationTime, ExitTime, KernelTime, UserTime;
155 // clang-format off
156 if (!GetProcessTimes(GetCurrentThread(), &CreationTime,
157 &ExitTime, &KernelTime, &UserTime))
158 // clang-format on
159 return -1;
160 // Add the time spent in kernel mode and the time spent in user
161 // mode to obtain the total thread time.
162 ULARGE_INTEGER kt = { .LowPart = KernelTime.dwLowDateTime,
163 .HighPart = KernelTime.dwHighDateTime };
164 ULARGE_INTEGER ut = { .LowPart = UserTime.dwLowDateTime,
165 .HighPart = UserTime.dwHighDateTime };
166 if (kt.QuadPart + ut.QuadPart > _I64_MAX) {
167 errno = EOVERFLOW;
168 return -1;
169 }
170 ft = kt.QuadPart + ut.QuadPart;
171 break;
172 }
173 default: errno = EINVAL; return -1;
174 }
175
176 LONGLONG sec = ft / 10000000l;
177 if (sec < TIME_T_MIN || sec > TIME_T_MAX) {
178 errno = EOVERFLOW;
179 return -1;
180 }
181
182 if (tp) {
183 tp->tv_sec = sec;
184 tp->tv_nsec = (ft % 10000000l) * 100;
185 }
186
187 return 0;
188}
189
190int
191clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp,
192 struct timespec *rmtp)
193{
194 switch (clock_id) {
195 case CLOCK_REALTIME:
196 case CLOCK_MONOTONIC: break;
197 case CLOCK_PROCESS_CPUTIME_ID: return ENOTSUP;
198 case CLOCK_THREAD_CPUTIME_ID:
199 default: return EINVAL;
200 }
201
202 if (rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000l)
203 return EINVAL;
204
205 int errsv = errno;
206
207 struct timespec now = { 0, 0 };
208 if (clock_gettime(clock_id, &now) == -1) {
209 int result = errno;
210 errno = errsv;
211 return result;
212 }
213
214 // Compute the absolute timeout while avoiding integer overflow.
215 struct timespec tp = *rqtp;
216 if (!(flags & TIMER_ABSTIME)) {
217 if (tp.tv_sec < 0 || (!tp.tv_sec && !tp.tv_nsec))
218 return 0;
219 if (now.tv_sec > TIME_T_MAX - tp.tv_sec)
220 return EINVAL;
221 tp.tv_sec += now.tv_sec;
222 tp.tv_nsec += now.tv_nsec;
223 if (tp.tv_nsec >= 1000000000l) {
224 if (tp.tv_sec == TIME_T_MAX)
225 return EINVAL;
226 tp.tv_sec++;
227 tp.tv_nsec -= 1000000000l;
228 }
229 }
230
231 // clang-format off
232 while (now.tv_sec < tp.tv_sec || (now.tv_sec == tp.tv_sec
233 && now.tv_nsec < tp.tv_nsec)) {
234 // clang-format on
235 // Round up to the nearest number of milliseconds, to make sure
236 // we don't wake up too early.
237 LONGLONG llMilliseconds =
238 (LONGLONG)(tp.tv_sec - now.tv_sec) * 1000
239 + (tp.tv_nsec - now.tv_nsec + 999999l)
240 / 1000000l;
241 DWORD dwMilliseconds = llMilliseconds <= MAX_SLEEP_MS
242 ? (DWORD)llMilliseconds
243 : MAX_SLEEP_MS;
244 DWORD dwResult = SleepEx(dwMilliseconds, TRUE);
245 if (clock_gettime(clock_id, &now) == -1) {
246 int result = errno;
247 errno = errsv;
248 return result;
249 }
250 if (dwResult) {
251 // In case of an interrupted relative sleep, compute the
252 // remaining time.
253 if (!(flags & TIMER_ABSTIME) && rmtp) {
254 rmtp->tv_sec = tp.tv_sec - now.tv_sec;
255 rmtp->tv_nsec = tp.tv_nsec - now.tv_nsec;
256 if (rmtp->tv_nsec < 0) {
257 rmtp->tv_sec--;
258 rmtp->tv_nsec += 1000000000l;
259 }
260 }
261 return EINTR;
262 }
263 }
264
265 return 0;
266}
267
268int
269clock_settime(clockid_t clock_id, const struct timespec *tp)
270{
271 if (clock_id != CLOCK_REALTIME) {
272 errno = EINVAL;
273 return -1;
274 }
275
276 if (tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000l) {
277 errno = EINVAL;
278 return -1;
279 }
280
281 if (tp->tv_sec + FILETIME_EPOCH < 0) {
282 errno = EINVAL;
283 return -1;
284 }
285 ULARGE_INTEGER li = { .QuadPart = tp->tv_sec + FILETIME_EPOCH };
286 if (li.QuadPart > _UI64_MAX / 10000000ul) {
287 errno = EINVAL;
288 return -1;
289 }
290 li.QuadPart *= 10000000ul;
291 if (li.QuadPart > _UI64_MAX - tp->tv_nsec / 100) {
292 errno = EINVAL;
293 return -1;
294 }
295 li.QuadPart += tp->tv_nsec / 100;
296
297 FILETIME ft = { li.LowPart, li.HighPart };
298 SYSTEMTIME st;
299 if (!FileTimeToSystemTime(&ft, &st)) {
300 errno = EINVAL;
301 return -1;
302 }
303 if (!SetSystemTime(&st)) {
304 errno = EPERM;
305 return -1;
306 }
307
308 return 0;
309}
310
311#endif // _WIN32 && !__MINGW32__
312
313#endif // !LELY_NO_RT
This header file is part of the C11 and POSIX compatibility library; it includes <time....
This is the internal header file of the C11 and POSIX compatibility library.
A time type with nanosecond resolution.
Definition time.h:88
long tv_nsec
Nanoseconds [0, 999999999].
Definition time.h:92
time_t tv_sec
Whole seconds (>= 0).
Definition time.h:90
int clockid_t
Used for clock ID type in the clock and timer functions.
Definition types.h:40