1 //===--- clock_gettime windows implementation -------------------*- 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/time_macros.h" 10 11 #include "src/__support/CPP/atomic.h" 12 #include "src/__support/CPP/bit.h" 13 #include "src/__support/CPP/limits.h" 14 #include "src/__support/macros/optimization.h" 15 #include "src/__support/time/clock_gettime.h" 16 #include "src/__support/time/units.h" 17 #include "src/__support/time/windows/performance_counter.h" 18 19 #define WIN32_LEAN_AND_MEAN 20 #define NOMINMAX 21 #include <Windows.h> 22 23 namespace LIBC_NAMESPACE_DECL { 24 namespace internal { 25 ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) { 26 using namespace time_units; 27 constexpr unsigned long long HNS_PER_SEC = 1_s_ns / 100ULL; 28 constexpr long long SEC_LIMIT = 29 cpp::numeric_limits<decltype(ts->tv_sec)>::max(); 30 ErrorOr<int> ret = 0; 31 switch (clockid) { 32 default: 33 ret = cpp::unexpected(EINVAL); 34 break; 35 36 case CLOCK_MONOTONIC: { 37 // see 38 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps 39 // Is the performance counter monotonic (non-decreasing)? 40 // Yes. performance_counter does not go backward. 41 [[clang::uninitialized]] LARGE_INTEGER buffer; 42 // On systems that run Windows XP or later, the function will always 43 // succeed and will thus never return zero. 44 ::QueryPerformanceCounter(&buffer); 45 long long freq = performance_counter::get_ticks_per_second(); 46 long long ticks = buffer.QuadPart; 47 long long tv_sec = ticks / freq; 48 long long tv_nsec = (ticks % freq) * 1_s_ns / freq; 49 if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) { 50 ret = cpp::unexpected(EOVERFLOW); 51 break; 52 } 53 ts->tv_sec = static_cast<decltype(ts->tv_sec)>(tv_sec); 54 ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(tv_nsec); 55 break; 56 } 57 case CLOCK_REALTIME: { 58 // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime 59 // GetSystemTimePreciseAsFileTime 60 // This function is best suited for high-resolution time-of-day 61 // measurements, or time stamps that are synchronized to UTC 62 [[clang::uninitialized]] FILETIME file_time; 63 [[clang::uninitialized]] ULARGE_INTEGER time; 64 ::GetSystemTimePreciseAsFileTime(&file_time); 65 time.LowPart = file_time.dwLowDateTime; 66 time.HighPart = file_time.dwHighDateTime; 67 68 // adjust to POSIX epoch (from Jan 1, 1601 to Jan 1, 1970) 69 constexpr unsigned long long POSIX_TIME_SHIFT = 70 (11644473600ULL * HNS_PER_SEC); 71 if (LIBC_UNLIKELY(POSIX_TIME_SHIFT > time.QuadPart)) { 72 ret = cpp::unexpected(EOVERFLOW); 73 break; 74 } 75 time.QuadPart -= (11644473600ULL * HNS_PER_SEC); 76 unsigned long long tv_sec = time.QuadPart / HNS_PER_SEC; 77 unsigned long long tv_nsec = (time.QuadPart % HNS_PER_SEC) * 100ULL; 78 if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) { 79 ret = cpp::unexpected(EOVERFLOW); 80 break; 81 } 82 ts->tv_sec = static_cast<decltype(ts->tv_sec)>(tv_sec); 83 ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(tv_nsec); 84 break; 85 } 86 case CLOCK_PROCESS_CPUTIME_ID: 87 case CLOCK_THREAD_CPUTIME_ID: { 88 [[clang::uninitialized]] FILETIME creation_time; 89 [[clang::uninitialized]] FILETIME exit_time; 90 [[clang::uninitialized]] FILETIME kernel_time; 91 [[clang::uninitialized]] FILETIME user_time; 92 bool success; 93 if (clockid == CLOCK_PROCESS_CPUTIME_ID) { 94 // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes 95 success = ::GetProcessTimes(::GetCurrentProcess(), &creation_time, 96 &exit_time, &kernel_time, &user_time); 97 } else { 98 // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes 99 success = ::GetThreadTimes(::GetCurrentThread(), &creation_time, 100 &exit_time, &kernel_time, &user_time); 101 } 102 if (!success) { 103 ret = cpp::unexpected(EINVAL); 104 break; 105 } 106 // https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime 107 // It is not recommended that you add and subtract values from the FILETIME 108 // structure to obtain relative times. Instead, you should copy the low- and 109 // high-order parts of the file time to a ULARGE_INTEGER structure, perform 110 // 64-bit arithmetic on the QuadPart member, and copy the LowPart and 111 // HighPart members into the FILETIME structure. 112 auto kernel_time_hns = cpp::bit_cast<ULARGE_INTEGER>(kernel_time); 113 auto user_time_hns = cpp::bit_cast<ULARGE_INTEGER>(user_time); 114 unsigned long long total_time_hns = 115 kernel_time_hns.QuadPart + user_time_hns.QuadPart; 116 117 unsigned long long tv_sec = total_time_hns / HNS_PER_SEC; 118 unsigned long long tv_nsec = (total_time_hns % HNS_PER_SEC) * 100ULL; 119 120 if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) { 121 ret = cpp::unexpected(EOVERFLOW); 122 break; 123 } 124 125 ts->tv_sec = static_cast<decltype(ts->tv_sec)>(tv_sec); 126 ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(tv_nsec); 127 128 break; 129 } 130 } 131 return ret; 132 } 133 } // namespace internal 134 } // namespace LIBC_NAMESPACE_DECL 135