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
29namespace LIBC_NAMESPACE_DECL {
30LLVM_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

source code of libc/src/time/windows/clock_getres.cpp