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/macros/optimization.h" 17 #include "src/__support/time/units.h" 18 #include "src/__support/time/windows/performance_counter.h" 19 #include "src/errno/libc_errno.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