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