[libc-commits] [libc] [libc] add timeout and clock conversion utilities (PR #91905)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Sun May 12 16:38:40 PDT 2024
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/91905
>From e5a3e11e4a3bab58772a6efee5c779d9b3290ffc Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 12 May 2024 19:19:26 -0400
Subject: [PATCH 1/3] [libc] add timeout and clock conversion utilities
---
libc/src/__support/time/linux/CMakeLists.txt | 25 +++++++-
.../__support/time/linux/clock_conversion.h | 40 ++++++++++++
.../__support/time/linux/clock_gettime.cpp | 35 -----------
libc/src/__support/time/linux/clock_gettime.h | 24 ++++++-
libc/src/__support/time/linux/timeout.h | 60 ++++++++++++++++++
libc/test/src/__support/CMakeLists.txt | 1 +
libc/test/src/__support/time/CMakeLists.txt | 5 ++
.../src/__support/time/linux/CMakeLists.txt | 8 +++
.../src/__support/time/linux/timeout_test.cpp | 63 +++++++++++++++++++
9 files changed, 221 insertions(+), 40 deletions(-)
create mode 100644 libc/src/__support/time/linux/clock_conversion.h
delete mode 100644 libc/src/__support/time/linux/clock_gettime.cpp
create mode 100644 libc/src/__support/time/linux/timeout.h
create mode 100644 libc/test/src/__support/time/CMakeLists.txt
create mode 100644 libc/test/src/__support/time/linux/CMakeLists.txt
create mode 100644 libc/test/src/__support/time/linux/timeout_test.cpp
diff --git a/libc/src/__support/time/linux/CMakeLists.txt b/libc/src/__support/time/linux/CMakeLists.txt
index f04d550555e19..2e3e3cf439d7c 100644
--- a/libc/src/__support/time/linux/CMakeLists.txt
+++ b/libc/src/__support/time/linux/CMakeLists.txt
@@ -1,9 +1,7 @@
-add_object_library(
+add_header_library(
clock_gettime
HDRS
clock_gettime.h
- SRCS
- clock_gettime.cpp
DEPENDS
libc.include.sys_syscall
libc.hdr.types.struct_timespec
@@ -12,3 +10,24 @@ add_object_library(
libc.src.__support.error_or
libc.src.__support.OSUtil.osutil
)
+
+add_header_library(
+ clock_conversion
+ HDRS
+ clock_conversion.h
+ DEPENDS
+ .clock_gettime
+ libc.src.__support.time.units
+)
+
+add_header_library(
+ timeout
+ HDRS
+ timeout.h
+ DEPENDS
+ .clock_gettime
+ .clock_conversion
+ libc.hdr.types.struct_timespec
+ libc.hdr.time_macros
+ libc.src.__support.CPP.expected
+)
diff --git a/libc/src/__support/time/linux/clock_conversion.h b/libc/src/__support/time/linux/clock_conversion.h
new file mode 100644
index 0000000000000..5d45ec6d1cfea
--- /dev/null
+++ b/libc/src/__support/time/linux/clock_conversion.h
@@ -0,0 +1,40 @@
+//===--- clock conversion linux implementation ------------------*- 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___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
+#include "src/__support/time/linux/clock_gettime.h"
+#include "src/__support/time/units.h"
+namespace LIBC_NAMESPACE {
+namespace internal {
+
+LIBC_INLINE timespec convert_clock(timespec input, clockid_t from,
+ clockid_t to) {
+ using namespace time_units;
+ timespec from_time;
+ timespec to_time;
+ timespec output;
+ internal::clock_gettime(from, &from_time);
+ internal::clock_gettime(to, &to_time);
+ output.tv_sec = input.tv_sec - from_time.tv_sec + to_time.tv_sec;
+ output.tv_nsec = input.tv_nsec - from_time.tv_nsec + to_time.tv_nsec;
+
+ if (output.tv_nsec > 1_s_ns) {
+ output.tv_sec++;
+ output.tv_nsec -= 1_s_ns;
+ } else if (output.tv_nsec < 0) {
+ output.tv_sec--;
+ output.tv_nsec += 1_s_ns;
+ }
+ return output;
+}
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
diff --git a/libc/src/__support/time/linux/clock_gettime.cpp b/libc/src/__support/time/linux/clock_gettime.cpp
deleted file mode 100644
index 7f266b282a391..0000000000000
--- a/libc/src/__support/time/linux/clock_gettime.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-//===--- clock_gettime linux implementation ---------------------*- 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 "src/__support/time/linux/clock_gettime.h"
-#include "src/__support/OSUtil/syscall.h"
-#include <sys/syscall.h>
-namespace LIBC_NAMESPACE {
-namespace internal {
-ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
-#if SYS_clock_gettime
- int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime,
- static_cast<long>(clockid),
- reinterpret_cast<long>(ts));
-#elif defined(SYS_clock_gettime64)
- static_assert(
- sizeof(time_t) == sizeof(int64_t),
- "SYS_clock_gettime64 requires struct timespec with 64-bit members.");
- int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64,
- static_cast<long>(clockid),
- reinterpret_cast<long>(ts));
-#else
-#error "SYS_clock_gettime and SYS_clock_gettime64 syscalls not available."
-#endif
- if (ret < 0)
- return Error(-ret);
- return ret;
-}
-
-} // namespace internal
-} // namespace LIBC_NAMESPACE
diff --git a/libc/src/__support/time/linux/clock_gettime.h b/libc/src/__support/time/linux/clock_gettime.h
index b1572726f6301..4cb6c560fb644 100644
--- a/libc/src/__support/time/linux/clock_gettime.h
+++ b/libc/src/__support/time/linux/clock_gettime.h
@@ -10,14 +10,34 @@
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
#include "hdr/types/clockid_t.h"
#include "hdr/types/struct_timespec.h"
+#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
-
#include "src/__support/error_or.h"
+#include <sys/syscall.h>
namespace LIBC_NAMESPACE {
namespace internal {
-ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts);
+LIBC_INLINE ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
+#if SYS_clock_gettime
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime,
+ static_cast<long>(clockid),
+ reinterpret_cast<long>(ts));
+#elif defined(SYS_clock_gettime64)
+ static_assert(
+ sizeof(time_t) == sizeof(int64_t),
+ "SYS_clock_gettime64 requires struct timespec with 64-bit members.");
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64,
+ static_cast<long>(clockid),
+ reinterpret_cast<long>(ts));
+#else
+#error "SYS_clock_gettime and SYS_clock_gettime64 syscalls not available."
+#endif
+ if (ret < 0)
+ return Error(-ret);
+ return ret;
}
+
+} // namespace internal
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
diff --git a/libc/src/__support/time/linux/timeout.h b/libc/src/__support/time/linux/timeout.h
new file mode 100644
index 0000000000000..7d3df936ff10c
--- /dev/null
+++ b/libc/src/__support/time/linux/timeout.h
@@ -0,0 +1,60 @@
+//===--- timeout linux implementation ---------------------------*- 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___SUPPORT_TIME_LINUX_TIMEOUT_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_H
+#include "hdr/time_macros.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/CPP/expected.h"
+#include "src/__support/time/linux/clock_conversion.h"
+#include "src/__support/time/units.h"
+namespace LIBC_NAMESPACE {
+namespace internal {
+// We use AbsTimeout to remind ourselves that the timeout is an absolute time.
+// This is a simple wrapper around the timespec struct that also keeps track of
+// whether the time is in realtime or monotonic time.
+
+// This struct is going to be used in timed locks. Pthread generally uses
+// realtime clocks for timeouts. However, due to non-monotoncity, realtime
+// clocks reportedly lead to undesired behaviors. Therefore, we also provide a
+// method to convert the timespec to a monotonic clock relative to the time of
+// function call.
+class AbsTimeout {
+ timespec timeout;
+ bool realtime_flag;
+ LIBC_INLINE constexpr explicit AbsTimeout(timespec ts, bool realtime)
+ : timeout(ts), realtime_flag(realtime) {}
+
+public:
+ enum class Error { Invalid, BeforeEpoch };
+ LIBC_INLINE timespec get_timespec() const { return timeout; }
+ LIBC_INLINE bool is_realtime() const { return realtime_flag; }
+ LIBC_INLINE static constexpr cpp::expected<AbsTimeout, Error>
+ from_timespec(timespec ts, bool realtime) {
+ using namespace time_units;
+ if (ts.tv_nsec < 0 || ts.tv_nsec >= 1_s_ns)
+ return cpp::unexpected<Error>(Error::Invalid);
+
+ // POSIX allows tv_sec to be negative. We interpret this as an expired
+ // timeout.
+ if (ts.tv_sec < 0)
+ return cpp::unexpected<Error>(Error::BeforeEpoch);
+
+ return AbsTimeout{ts, realtime};
+ }
+ LIBC_INLINE void ensure_monotonic() {
+ if (realtime_flag) {
+ timeout = convert_clock(timeout, CLOCK_REALTIME, CLOCK_MONOTONIC);
+ realtime_flag = false;
+ }
+ }
+};
+} // namespace internal
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_H
diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt
index 5d1230f5f3a70..8bdc56ee59ccc 100644
--- a/libc/test/src/__support/CMakeLists.txt
+++ b/libc/test/src/__support/CMakeLists.txt
@@ -206,3 +206,4 @@ add_subdirectory(OSUtil)
add_subdirectory(FPUtil)
add_subdirectory(fixed_point)
add_subdirectory(HashTable)
+add_subdirectory(time)
diff --git a/libc/test/src/__support/time/CMakeLists.txt b/libc/test/src/__support/time/CMakeLists.txt
new file mode 100644
index 0000000000000..37062e131acf5
--- /dev/null
+++ b/libc/test/src/__support/time/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_custom_target(libc-support-time-tests)
+
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${LIBC_TARGET_OS})
+endif()
diff --git a/libc/test/src/__support/time/linux/CMakeLists.txt b/libc/test/src/__support/time/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..8c95667824a9c
--- /dev/null
+++ b/libc/test/src/__support/time/linux/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_libc_test(
+ timeout_test
+ SUITE libc-support-time-tests
+ SRCS timeout_test.cpp
+ DEPENDS
+ libc.src.__support.time.linux.timeout
+ libc.src.__support.CPP.expected
+)
diff --git a/libc/test/src/__support/time/linux/timeout_test.cpp b/libc/test/src/__support/time/linux/timeout_test.cpp
new file mode 100644
index 0000000000000..f2a27dc6d0ab7
--- /dev/null
+++ b/libc/test/src/__support/time/linux/timeout_test.cpp
@@ -0,0 +1,63 @@
+//===-- unit tests for linux's timeout utilities --------------------------===//
+//
+// 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/expected.h"
+#include "src/__support/time/linux/timeout.h"
+
+#include "test/UnitTest/Test.h"
+
+namespace LIBC_NAMESPACE {
+namespace internal {
+TEST(LlvmLibcSupportLinuxTimeoutTest, NegativeSecond) {
+ timespec ts = {-1, 0};
+ cpp::expected<AbsTimeout, AbsTimeout::Error> result =
+ AbsTimeout::from_timespec(ts, false);
+ ASSERT_FALSE(result.has_value());
+ ASSERT_EQ(result.error(), AbsTimeout::Error::BeforeEpoch);
+}
+TEST(LlvmLibcSupportLinuxTimeoutTest, OverflowNano) {
+ using namespace time_units;
+ timespec ts = {0, 2_s_ns};
+ cpp::expected<AbsTimeout, AbsTimeout::Error> result =
+ AbsTimeout::from_timespec(ts, false);
+ ASSERT_FALSE(result.has_value());
+ ASSERT_EQ(result.error(), AbsTimeout::Error::Invalid);
+}
+TEST(LlvmLibcSupportLinuxTimeoutTest, UnderflowNano) {
+ using namespace time_units;
+ timespec ts = {0, -1};
+ cpp::expected<AbsTimeout, AbsTimeout::Error> result =
+ AbsTimeout::from_timespec(ts, false);
+ ASSERT_FALSE(result.has_value());
+ ASSERT_EQ(result.error(), AbsTimeout::Error::Invalid);
+}
+TEST(LlvmLibcSupportLinuxTimeoutTest, NoChangeIfClockIsMonotonic) {
+ using namespace time_units;
+ timespec ts = {10000, 0};
+ cpp::expected<AbsTimeout, AbsTimeout::Error> result =
+ AbsTimeout::from_timespec(ts, false);
+ ASSERT_TRUE(result.has_value());
+ result->ensure_monotonic();
+ ASSERT_FALSE(result->is_realtime());
+ ASSERT_EQ(result->get_timespec().tv_sec, static_cast<time_t>(10000));
+ ASSERT_EQ(result->get_timespec().tv_nsec, static_cast<time_t>(0));
+}
+TEST(LlvmLibcSupportLinuxTimeoutTest, ValidAfterConversion) {
+ using namespace time_units;
+ timespec ts;
+ internal::clock_gettime(CLOCK_REALTIME, &ts);
+ cpp::expected<AbsTimeout, AbsTimeout::Error> result =
+ AbsTimeout::from_timespec(ts, true);
+ ASSERT_TRUE(result.has_value());
+ result->ensure_monotonic();
+ ASSERT_FALSE(result->is_realtime());
+ ASSERT_TRUE(
+ AbsTimeout::from_timespec(result->get_timespec(), false).has_value());
+}
+} // namespace internal
+} // namespace LIBC_NAMESPACE
>From 8407731e7614a047afd607ac7f48573300be3f0e Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 12 May 2024 19:36:27 -0400
Subject: [PATCH 2/3] [libc] remove extra using namespace
---
libc/test/src/__support/time/linux/timeout_test.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/libc/test/src/__support/time/linux/timeout_test.cpp b/libc/test/src/__support/time/linux/timeout_test.cpp
index f2a27dc6d0ab7..ff849feeb2847 100644
--- a/libc/test/src/__support/time/linux/timeout_test.cpp
+++ b/libc/test/src/__support/time/linux/timeout_test.cpp
@@ -29,7 +29,6 @@ TEST(LlvmLibcSupportLinuxTimeoutTest, OverflowNano) {
ASSERT_EQ(result.error(), AbsTimeout::Error::Invalid);
}
TEST(LlvmLibcSupportLinuxTimeoutTest, UnderflowNano) {
- using namespace time_units;
timespec ts = {0, -1};
cpp::expected<AbsTimeout, AbsTimeout::Error> result =
AbsTimeout::from_timespec(ts, false);
@@ -37,7 +36,6 @@ TEST(LlvmLibcSupportLinuxTimeoutTest, UnderflowNano) {
ASSERT_EQ(result.error(), AbsTimeout::Error::Invalid);
}
TEST(LlvmLibcSupportLinuxTimeoutTest, NoChangeIfClockIsMonotonic) {
- using namespace time_units;
timespec ts = {10000, 0};
cpp::expected<AbsTimeout, AbsTimeout::Error> result =
AbsTimeout::from_timespec(ts, false);
@@ -48,7 +46,6 @@ TEST(LlvmLibcSupportLinuxTimeoutTest, NoChangeIfClockIsMonotonic) {
ASSERT_EQ(result->get_timespec().tv_nsec, static_cast<time_t>(0));
}
TEST(LlvmLibcSupportLinuxTimeoutTest, ValidAfterConversion) {
- using namespace time_units;
timespec ts;
internal::clock_gettime(CLOCK_REALTIME, &ts);
cpp::expected<AbsTimeout, AbsTimeout::Error> result =
>From 623352b426884f1d672fb4a3fc772113a82a1e51 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 12 May 2024 19:38:23 -0400
Subject: [PATCH 3/3] [libc] add whitespace for clarity
---
libc/src/__support/time/linux/clock_conversion.h | 2 ++
libc/src/__support/time/linux/clock_gettime.h | 1 +
libc/src/__support/time/linux/timeout.h | 2 ++
libc/test/src/__support/time/linux/timeout_test.cpp | 1 -
4 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/time/linux/clock_conversion.h b/libc/src/__support/time/linux/clock_conversion.h
index 5d45ec6d1cfea..4a7c8ff284849 100644
--- a/libc/src/__support/time/linux/clock_conversion.h
+++ b/libc/src/__support/time/linux/clock_conversion.h
@@ -8,8 +8,10 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
+
#include "src/__support/time/linux/clock_gettime.h"
#include "src/__support/time/units.h"
+
namespace LIBC_NAMESPACE {
namespace internal {
diff --git a/libc/src/__support/time/linux/clock_gettime.h b/libc/src/__support/time/linux/clock_gettime.h
index 4cb6c560fb644..bbdde98551abe 100644
--- a/libc/src/__support/time/linux/clock_gettime.h
+++ b/libc/src/__support/time/linux/clock_gettime.h
@@ -8,6 +8,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
+
#include "hdr/types/clockid_t.h"
#include "hdr/types/struct_timespec.h"
#include "src/__support/OSUtil/syscall.h"
diff --git a/libc/src/__support/time/linux/timeout.h b/libc/src/__support/time/linux/timeout.h
index 7d3df936ff10c..266211ace2e6b 100644
--- a/libc/src/__support/time/linux/timeout.h
+++ b/libc/src/__support/time/linux/timeout.h
@@ -8,11 +8,13 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_H
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_H
+
#include "hdr/time_macros.h"
#include "hdr/types/struct_timespec.h"
#include "src/__support/CPP/expected.h"
#include "src/__support/time/linux/clock_conversion.h"
#include "src/__support/time/units.h"
+
namespace LIBC_NAMESPACE {
namespace internal {
// We use AbsTimeout to remind ourselves that the timeout is an absolute time.
diff --git a/libc/test/src/__support/time/linux/timeout_test.cpp b/libc/test/src/__support/time/linux/timeout_test.cpp
index ff849feeb2847..8c638dd7bb777 100644
--- a/libc/test/src/__support/time/linux/timeout_test.cpp
+++ b/libc/test/src/__support/time/linux/timeout_test.cpp
@@ -8,7 +8,6 @@
#include "src/__support/CPP/expected.h"
#include "src/__support/time/linux/timeout.h"
-
#include "test/UnitTest/Test.h"
namespace LIBC_NAMESPACE {
More information about the libc-commits
mailing list