Lely core libraries  2.2.5
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 
54 int
55 clock_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 
92 int
93 clock_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 
190 int
191 clock_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 
268 int
269 clock_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.