[libc-commits] [libc] 9a06fb7 - [libc][time][windows] implement clock_getres (#118931)
via libc-commits
libc-commits at lists.llvm.org
Mon Dec 9 19:00:04 PST 2024
Author: Schrodinger ZHU Yifan
Date: 2024-12-09T22:00:01-05:00
New Revision: 9a06fb7e5c00d1379688645c2c28955664016278
URL: https://github.com/llvm/llvm-project/commit/9a06fb7e5c00d1379688645c2c28955664016278
DIFF: https://github.com/llvm/llvm-project/commit/9a06fb7e5c00d1379688645c2c28955664016278.diff
LOG: [libc][time][windows] implement clock_getres (#118931)
Added:
libc/src/__support/time/windows/performance_counter.h
libc/src/time/clock_getres.h
libc/src/time/windows/CMakeLists.txt
libc/src/time/windows/clock_getres.cpp
libc/test/src/time/clock_getres_test.cpp
Modified:
libc/config/windows/entrypoints.txt
libc/src/__support/time/windows/CMakeLists.txt
libc/src/__support/time/windows/clock_gettime.cpp
libc/src/time/CMakeLists.txt
libc/test/src/time/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index d0796b85aec2af..83a64e935cd02c 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -98,6 +98,7 @@ set(TARGET_LIBC_ENTRYPOINTS
# time.h entrypoints
libc.src.time.time
+ libc.src.time.clock_getres
)
set(TARGET_LIBM_ENTRYPOINTS
diff --git a/libc/src/__support/time/windows/CMakeLists.txt b/libc/src/__support/time/windows/CMakeLists.txt
index 0f557ed8800802..dd0ac2f2f79aee 100644
--- a/libc/src/__support/time/windows/CMakeLists.txt
+++ b/libc/src/__support/time/windows/CMakeLists.txt
@@ -1,3 +1,12 @@
+add_header_library(
+ performance_counter
+ HDRS
+ performance_counter.h
+ DEPENDS
+ libc.src.__support.CPP.atomic
+ libc.src.__support.common
+)
+
add_object_library(
clock_gettime
HDRS
@@ -5,6 +14,7 @@ add_object_library(
SRCS
clock_gettime.cpp
DEPENDS
+ .performance_counter
libc.hdr.types.struct_timespec
libc.hdr.types.clockid_t
libc.hdr.errno_macros
diff --git a/libc/src/__support/time/windows/clock_gettime.cpp b/libc/src/__support/time/windows/clock_gettime.cpp
index c2536acf37d799..813eae0df97050 100644
--- a/libc/src/__support/time/windows/clock_gettime.cpp
+++ b/libc/src/__support/time/windows/clock_gettime.cpp
@@ -14,6 +14,7 @@
#include "src/__support/macros/optimization.h"
#include "src/__support/time/clock_gettime.h"
#include "src/__support/time/units.h"
+#include "src/__support/time/windows/performance_counter.h"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
@@ -21,23 +22,6 @@
namespace LIBC_NAMESPACE_DECL {
namespace internal {
-static long long get_ticks_per_second() {
- static cpp::Atomic<long long> frequency = 0;
- // Relaxed ordering is enough. It is okay to record the frequency multiple
- // times. The store operation itself is atomic and the value must propagate
- // as required by cache coherence.
- auto freq = frequency.load(cpp::MemoryOrder::RELAXED);
- if (!freq) {
- [[clang::uninitialized]] LARGE_INTEGER buffer;
- // On systems that run Windows XP or later, the function will always
- // succeed and will thus never return zero.
- ::QueryPerformanceFrequency(&buffer);
- frequency.store(buffer.QuadPart, cpp::MemoryOrder::RELAXED);
- return buffer.QuadPart;
- }
- return freq;
-}
-
ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
using namespace time_units;
constexpr unsigned long long HNS_PER_SEC = 1_s_ns / 100ULL;
@@ -53,12 +37,12 @@ ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
// see
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
// Is the performance counter monotonic (non-decreasing)?
- // Yes. QPC does not go backward.
+ // Yes. performance_counter does not go backward.
[[clang::uninitialized]] LARGE_INTEGER buffer;
// On systems that run Windows XP or later, the function will always
// succeed and will thus never return zero.
::QueryPerformanceCounter(&buffer);
- long long freq = get_ticks_per_second();
+ long long freq = performance_counter::get_ticks_per_second();
long long ticks = buffer.QuadPart;
long long tv_sec = ticks / freq;
long long tv_nsec = (ticks % freq) * 1_s_ns / freq;
diff --git a/libc/src/__support/time/windows/performance_counter.h b/libc/src/__support/time/windows/performance_counter.h
new file mode 100644
index 00000000000000..3df81b3efff401
--- /dev/null
+++ b/libc/src/__support/time/windows/performance_counter.h
@@ -0,0 +1,35 @@
+//===--- Cached Performance Counter Frequency ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/common.h"
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace performance_counter {
+LIBC_INLINE long long get_ticks_per_second() {
+ static cpp::Atomic<long long> frequency = 0;
+ // Relaxed ordering is enough. It is okay to record the frequency multiple
+ // times. The store operation itself is atomic and the value must propagate
+ // as required by cache coherence.
+ auto freq = frequency.load(cpp::MemoryOrder::RELAXED);
+ if (!freq) {
+ [[clang::uninitialized]] LARGE_INTEGER buffer;
+ // On systems that run Windows XP or later, the function will always
+ // succeed and will thus never return zero.
+ ::QueryPerformanceFrequency(&buffer);
+ frequency.store(buffer.QuadPart, cpp::MemoryOrder::RELAXED);
+ return buffer.QuadPart;
+ }
+ return freq;
+}
+} // namespace performance_counter
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 3e8e6882ffc5d4..ae835dcc742742 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -151,3 +151,10 @@ add_entrypoint_object(
DEPENDS
.${LIBC_TARGET_OS}.gettimeofday
)
+
+add_entrypoint_object(
+ clock_getres
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.clock_getres
+)
diff --git a/libc/src/time/clock_getres.h b/libc/src/time/clock_getres.h
new file mode 100644
index 00000000000000..c1c581c5359949
--- /dev/null
+++ b/libc/src/time/clock_getres.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of clock_getres -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIBC_SRC_TIME_CLOCK_GETRES_H
+#define LLVM_LIBC_SRC_TIME_CLOCK_GETRES_H
+
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int clock_getres(clockid_t clockid, timespec *tp);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_CLOCK_GETRES_H
diff --git a/libc/src/time/windows/CMakeLists.txt b/libc/src/time/windows/CMakeLists.txt
new file mode 100644
index 00000000000000..6f242cd168f52e
--- /dev/null
+++ b/libc/src/time/windows/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_entrypoint_object(
+ clock_getres
+ SRCS
+ clock_getres.cpp
+ DEPENDS
+ libc.src.__support.time.windows.performance_counter
+ libc.src.__support.time.units
+ libc.src.__support.common
+ libc.src.__support.macros.optimization
+ libc.hdr.time_macros
+ libc.hdr.types.time_t
+ libc.hdr.types.struct_timespec
+)
diff --git a/libc/src/time/windows/clock_getres.cpp b/libc/src/time/windows/clock_getres.cpp
new file mode 100644
index 00000000000000..b8c0c82aa6419c
--- /dev/null
+++ b/libc/src/time/windows/clock_getres.cpp
@@ -0,0 +1,110 @@
+//===-- Windows implementation of clock_getres ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/time_macros.h"
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/struct_timespec.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/time/units.h"
+#include "src/__support/time/windows/performance_counter.h"
+#include "src/errno/libc_errno.h"
+#include "src/time/clock_getres.h"
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+
+// add in dependencies for GetSystemTimeAdjustmentPrecise
+#pragma comment(lib, "mincore.lib")
+
+namespace LIBC_NAMESPACE_DECL {
+LLVM_LIBC_FUNCTION(int, clock_getres, (clockid_t id, struct timespec *res)) {
+ using namespace time_units;
+ // POSIX allows nullptr to be passed as res, in which case the function should
+ // do nothing.
+ if (res == nullptr)
+ return 0;
+ constexpr unsigned long long HNS_PER_SEC = 1_s_ns / 100ULL;
+ constexpr unsigned long long SEC_LIMIT =
+ cpp::numeric_limits<decltype(res->tv_sec)>::max();
+ // For CLOCK_MONOTONIC, we are using performance counter
+ // https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
+ // Hence, the resolution is given by the performance counter frequency.
+ // For CLOCK_REALTIME, the precision is given by
+ // GetSystemTimeAdjustmentPrecise
+ // (https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustmentprecise)
+ // For CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID, the precision is
+ // given by GetSystemTimeAdjustment
+ // (https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustment)
+ switch (id) {
+ default:
+ libc_errno = EINVAL;
+ return -1;
+
+ case CLOCK_MONOTONIC: {
+ long long freq = performance_counter::get_ticks_per_second();
+ __builtin_assume(freq != 0);
+ // division of 1 second by frequency, rounded up.
+ long long tv_sec = static_cast<long long>(freq == 1);
+ long long tv_nsec =
+ LIBC_LIKELY(freq != 1) ? 1ll + ((1_s_ns - 1ll) / freq) : 0ll;
+ // not possible to overflow tv_sec, tv_nsec
+ res->tv_sec = static_cast<decltype(res->tv_sec)>(tv_sec);
+ res->tv_nsec = static_cast<decltype(res->tv_nsec)>(tv_nsec);
+ break;
+ }
+
+ case CLOCK_REALTIME: {
+ [[clang::uninitialized]] DWORD64 time_adjustment;
+ [[clang::uninitialized]] DWORD64 time_increment;
+ [[clang::uninitialized]] BOOL time_adjustment_disabled;
+ if (!::GetSystemTimeAdjustmentPrecise(&time_adjustment, &time_increment,
+ &time_adjustment_disabled)) {
+ libc_errno = EINVAL;
+ return -1;
+ }
+ DWORD64 tv_sec = time_increment / HNS_PER_SEC;
+ DWORD64 tv_nsec = (time_increment % HNS_PER_SEC) * 100ULL;
+ if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) {
+ libc_errno = EOVERFLOW;
+ return -1;
+ }
+ res->tv_sec = static_cast<decltype(res->tv_sec)>(tv_sec);
+ res->tv_nsec = static_cast<decltype(res->tv_nsec)>(tv_nsec);
+ break;
+ }
+ case CLOCK_PROCESS_CPUTIME_ID:
+ case CLOCK_THREAD_CPUTIME_ID: {
+ [[clang::uninitialized]] DWORD time_adjustment;
+ [[clang::uninitialized]] DWORD time_increment;
+ [[clang::uninitialized]] BOOL time_adjustment_disabled;
+ if (!::GetSystemTimeAdjustment(&time_adjustment, &time_increment,
+ &time_adjustment_disabled)) {
+ libc_errno = EINVAL;
+ return -1;
+ }
+ DWORD hns_per_sec = static_cast<DWORD>(HNS_PER_SEC);
+ DWORD sec_limit = static_cast<DWORD>(SEC_LIMIT);
+ DWORD tv_sec = time_increment / hns_per_sec;
+ DWORD tv_nsec = (time_increment % hns_per_sec) * 100UL;
+ if (LIBC_UNLIKELY(tv_sec > sec_limit)) {
+ libc_errno = EOVERFLOW;
+ return -1;
+ }
+ res->tv_sec = static_cast<decltype(res->tv_sec)>(tv_sec);
+ res->tv_nsec = static_cast<decltype(res->tv_nsec)>(tv_nsec);
+ break;
+ }
+ }
+ return 0;
+}
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index d2a98677a55435..da3903f3e0e494 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -71,11 +71,21 @@ add_libc_test(
SUITE
libc_time_unittests
SRCS
- clock_gettime_test.cpp
+ clock_gettime_test.cpp
DEPENDS
libc.src.time.clock_gettime
)
+add_libc_test(
+ clock_getres_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ clock_getres_test.cpp
+ DEPENDS
+ libc.src.time.clock_getres
+)
+
add_libc_unittest(
diff time_test
SUITE
diff --git a/libc/test/src/time/clock_getres_test.cpp b/libc/test/src/time/clock_getres_test.cpp
new file mode 100644
index 00000000000000..d8b3f01b37a38a
--- /dev/null
+++ b/libc/test/src/time/clock_getres_test.cpp
@@ -0,0 +1,55 @@
+//===-- Unittests for clock_getres- ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/time_macros.h"
+#include "src/time/clock_getres.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+TEST(LlvmLibcClockGetRes, Invalid) {
+ timespec tp;
+ EXPECT_THAT(LIBC_NAMESPACE::clock_getres(-1, &tp), Fails(EINVAL));
+}
+
+TEST(LlvmLibcClockGetRes, NullSpec) {
+ EXPECT_THAT(LIBC_NAMESPACE::clock_getres(CLOCK_REALTIME, nullptr),
+ Succeeds());
+}
+
+TEST(LlvmLibcClockGetRes, Realtime) {
+ timespec tp;
+ EXPECT_THAT(LIBC_NAMESPACE::clock_getres(CLOCK_REALTIME, &tp), Succeeds());
+ EXPECT_GE(tp.tv_sec, static_cast<decltype(tp.tv_sec)>(0));
+ EXPECT_GE(tp.tv_nsec, static_cast<decltype(tp.tv_nsec)>(0));
+}
+
+TEST(LlvmLibcClockGetRes, Monotonic) {
+ timespec tp;
+ ASSERT_THAT(LIBC_NAMESPACE::clock_getres(CLOCK_MONOTONIC, &tp), Succeeds());
+ EXPECT_GE(tp.tv_sec, static_cast<decltype(tp.tv_sec)>(0));
+ EXPECT_GE(tp.tv_nsec, static_cast<decltype(tp.tv_nsec)>(0));
+}
+
+TEST(LlvmLibcClockGetRes, ProcessCpuTime) {
+ timespec tp;
+ ASSERT_THAT(LIBC_NAMESPACE::clock_getres(CLOCK_PROCESS_CPUTIME_ID, &tp),
+ Succeeds());
+ EXPECT_GE(tp.tv_sec, static_cast<decltype(tp.tv_sec)>(0));
+ EXPECT_GE(tp.tv_nsec, static_cast<decltype(tp.tv_nsec)>(0));
+}
+
+TEST(LlvmLibcClockGetRes, ThreadCpuTime) {
+ timespec tp;
+ ASSERT_THAT(LIBC_NAMESPACE::clock_getres(CLOCK_THREAD_CPUTIME_ID, &tp),
+ Succeeds());
+ EXPECT_GE(tp.tv_sec, static_cast<decltype(tp.tv_sec)>(0));
+ EXPECT_GE(tp.tv_nsec, static_cast<decltype(tp.tv_nsec)>(0));
+}
More information about the libc-commits
mailing list