• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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