1 | //===-- Windows implementation of clock_getres ------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "hdr/errno_macros.h" |
10 | #include "hdr/time_macros.h" |
11 | #include "hdr/types/clockid_t.h" |
12 | #include "hdr/types/struct_timespec.h" |
13 | |
14 | #include "src/__support/CPP/limits.h" |
15 | #include "src/__support/common.h" |
16 | #include "src/__support/libc_errno.h" |
17 | #include "src/__support/macros/optimization.h" |
18 | #include "src/__support/time/units.h" |
19 | #include "src/__support/time/windows/performance_counter.h" |
20 | #include "src/time/clock_getres.h" |
21 | |
22 | #define WIN32_LEAN_AND_MEAN |
23 | #define NOMINMAX |
24 | #include <Windows.h> |
25 | |
26 | // add in dependencies for GetSystemTimeAdjustmentPrecise |
27 | #pragma comment(lib, "mincore.lib") |
28 | |
29 | namespace LIBC_NAMESPACE_DECL { |
30 | LLVM_LIBC_FUNCTION(int, clock_getres, (clockid_t id, struct timespec *res)) { |
31 | using namespace time_units; |
32 | // POSIX allows nullptr to be passed as res, in which case the function should |
33 | // do nothing. |
34 | if (res == nullptr) |
35 | return 0; |
36 | constexpr unsigned long long HNS_PER_SEC = 1_s_ns / 100ULL; |
37 | constexpr unsigned long long SEC_LIMIT = |
38 | cpp::numeric_limits<decltype(res->tv_sec)>::max(); |
39 | // For CLOCK_MONOTONIC, we are using performance counter |
40 | // https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps |
41 | // Hence, the resolution is given by the performance counter frequency. |
42 | // For CLOCK_REALTIME, the precision is given by |
43 | // GetSystemTimeAdjustmentPrecise |
44 | // (https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustmentprecise) |
45 | // For CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID, the precision is |
46 | // given by GetSystemTimeAdjustment |
47 | // (https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustment) |
48 | switch (id) { |
49 | default: |
50 | libc_errno = EINVAL; |
51 | return -1; |
52 | |
53 | case CLOCK_MONOTONIC: { |
54 | long long freq = performance_counter::get_ticks_per_second(); |
55 | __builtin_assume(freq != 0); |
56 | // division of 1 second by frequency, rounded up. |
57 | long long tv_sec = static_cast<long long>(freq == 1); |
58 | long long tv_nsec = |
59 | LIBC_LIKELY(freq != 1) ? 1ll + ((1_s_ns - 1ll) / freq) : 0ll; |
60 | // not possible to overflow tv_sec, tv_nsec |
61 | res->tv_sec = static_cast<decltype(res->tv_sec)>(tv_sec); |
62 | res->tv_nsec = static_cast<decltype(res->tv_nsec)>(tv_nsec); |
63 | break; |
64 | } |
65 | |
66 | case CLOCK_REALTIME: { |
67 | [[clang::uninitialized]] DWORD64 time_adjustment; |
68 | [[clang::uninitialized]] DWORD64 time_increment; |
69 | [[clang::uninitialized]] BOOL time_adjustment_disabled; |
70 | if (!::GetSystemTimeAdjustmentPrecise(&time_adjustment, &time_increment, |
71 | &time_adjustment_disabled)) { |
72 | libc_errno = EINVAL; |
73 | return -1; |
74 | } |
75 | DWORD64 tv_sec = time_increment / HNS_PER_SEC; |
76 | DWORD64 tv_nsec = (time_increment % HNS_PER_SEC) * 100ULL; |
77 | if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) { |
78 | libc_errno = EOVERFLOW; |
79 | return -1; |
80 | } |
81 | res->tv_sec = static_cast<decltype(res->tv_sec)>(tv_sec); |
82 | res->tv_nsec = static_cast<decltype(res->tv_nsec)>(tv_nsec); |
83 | break; |
84 | } |
85 | case CLOCK_PROCESS_CPUTIME_ID: |
86 | case CLOCK_THREAD_CPUTIME_ID: { |
87 | [[clang::uninitialized]] DWORD time_adjustment; |
88 | [[clang::uninitialized]] DWORD time_increment; |
89 | [[clang::uninitialized]] BOOL time_adjustment_disabled; |
90 | if (!::GetSystemTimeAdjustment(&time_adjustment, &time_increment, |
91 | &time_adjustment_disabled)) { |
92 | libc_errno = EINVAL; |
93 | return -1; |
94 | } |
95 | DWORD hns_per_sec = static_cast<DWORD>(HNS_PER_SEC); |
96 | DWORD sec_limit = static_cast<DWORD>(SEC_LIMIT); |
97 | DWORD tv_sec = time_increment / hns_per_sec; |
98 | DWORD tv_nsec = (time_increment % hns_per_sec) * 100UL; |
99 | if (LIBC_UNLIKELY(tv_sec > sec_limit)) { |
100 | libc_errno = EOVERFLOW; |
101 | return -1; |
102 | } |
103 | res->tv_sec = static_cast<decltype(res->tv_sec)>(tv_sec); |
104 | res->tv_nsec = static_cast<decltype(res->tv_nsec)>(tv_nsec); |
105 | break; |
106 | } |
107 | } |
108 | return 0; |
109 | } |
110 | } // namespace LIBC_NAMESPACE_DECL |
111 | |