[libc-commits] [libc] [libc] add timeout and clock conversion utilities (PR #91905)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Mon May 13 11:27:16 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/5] [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/5] [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/5] [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 {

>From 038fd55301767ec3af7177012becf354d503dbca Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Mon, 13 May 2024 09:52:13 -0400
Subject: [PATCH 4/5] separate timeout definition and use it in futex

---
 .../__support/threads/linux/CMakeLists.txt    |  2 +-
 .../src/__support/threads/linux/futex_utils.h | 11 ++--
 libc/src/__support/time/linux/CMakeLists.txt  | 12 +++-
 libc/src/__support/time/linux/timeout.h       | 38 ++----------
 libc/src/__support/time/linux/timeout_basic.h | 60 +++++++++++++++++++
 5 files changed, 81 insertions(+), 42 deletions(-)
 create mode 100644 libc/src/__support/time/linux/timeout_basic.h

diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 9bee30206f1b9..eec1421629b22 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -19,7 +19,7 @@ add_header_library(
     libc.src.__support.CPP.atomic
     libc.src.__support.CPP.limits
     libc.src.__support.CPP.optional
-    libc.hdr.types.struct_timespec
+    libc.src.__support.time.linux.timeout_basic
 )
 
 add_header_library(
diff --git a/libc/src/__support/threads/linux/futex_utils.h b/libc/src/__support/threads/linux/futex_utils.h
index 1fbce4f7bf438..45c725dc008f4 100644
--- a/libc/src/__support/threads/linux/futex_utils.h
+++ b/libc/src/__support/threads/linux/futex_utils.h
@@ -9,23 +9,20 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
 #define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
 
-#include "hdr/types/struct_timespec.h"
 #include "src/__support/CPP/atomic.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/optional.h"
 #include "src/__support/OSUtil/syscall.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/threads/linux/futex_word.h"
+#include "src/__support/time/linux/timeout_basic.h"
 #include <linux/errno.h>
 #include <linux/futex.h>
 
 namespace LIBC_NAMESPACE {
 class Futex : public cpp::Atomic<FutexWordType> {
 public:
-  struct Timeout {
-    timespec abs_time;
-    bool is_realtime;
-  };
+  using Timeout = internal::AbsTimeout;
   LIBC_INLINE constexpr Futex(FutexWordType value)
       : cpp::Atomic<FutexWordType>(value) {}
   LIBC_INLINE Futex &operator=(FutexWordType value) {
@@ -37,7 +34,7 @@ class Futex : public cpp::Atomic<FutexWordType> {
                         bool is_shared = false) {
     // use bitset variants to enforce abs_time
     uint32_t op = is_shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE;
-    if (timeout && timeout->is_realtime) {
+    if (timeout && timeout->is_realtime()) {
       op |= FUTEX_CLOCK_REALTIME;
     }
     for (;;) {
@@ -49,7 +46,7 @@ class Futex : public cpp::Atomic<FutexWordType> {
           /* futex address */ this,
           /* futex operation  */ op,
           /* expected value */ expected,
-          /* timeout */ timeout ? &timeout->abs_time : nullptr,
+          /* timeout */ timeout ? &timeout->get_timespec() : nullptr,
           /* ignored */ nullptr,
           /* bitset */ FUTEX_BITSET_MATCH_ANY);
 
diff --git a/libc/src/__support/time/linux/CMakeLists.txt b/libc/src/__support/time/linux/CMakeLists.txt
index 2e3e3cf439d7c..25aa48f3b6f36 100644
--- a/libc/src/__support/time/linux/CMakeLists.txt
+++ b/libc/src/__support/time/linux/CMakeLists.txt
@@ -20,6 +20,15 @@ add_header_library(
     libc.src.__support.time.units
 )
 
+add_header_library(
+  timeout_basic
+  HDRS
+    timeout_basic.h
+  DEPENDS
+    libc.hdr.types.struct_timespec
+    libc.src.__support.time.units
+)
+
 add_header_library(
   timeout
   HDRS
@@ -27,7 +36,6 @@ add_header_library(
   DEPENDS
     .clock_gettime
     .clock_conversion
-    libc.hdr.types.struct_timespec
-    libc.hdr.time_macros
+    .timeout_basic
     libc.src.__support.CPP.expected
 )
diff --git a/libc/src/__support/time/linux/timeout.h b/libc/src/__support/time/linux/timeout.h
index 266211ace2e6b..f5f3e8dc65771 100644
--- a/libc/src/__support/time/linux/timeout.h
+++ b/libc/src/__support/time/linux/timeout.h
@@ -10,10 +10,8 @@
 #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"
+#include "src/__support/time/linux/timeout_basic.h"
 
 namespace LIBC_NAMESPACE {
 namespace internal {
@@ -26,36 +24,12 @@ namespace internal {
 // 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;
-    }
+LIBC_INLINE void AbsTimeout::ensure_monotonic() {
+  if (realtime_flag) {
+    timeout = convert_clock(timeout, CLOCK_REALTIME, CLOCK_MONOTONIC);
+    realtime_flag = false;
   }
-};
+}
 } // namespace internal
 } // namespace LIBC_NAMESPACE
 
diff --git a/libc/src/__support/time/linux/timeout_basic.h b/libc/src/__support/time/linux/timeout_basic.h
new file mode 100644
index 0000000000000..68d3456366aa3
--- /dev/null
+++ b/libc/src/__support/time/linux/timeout_basic.h
@@ -0,0 +1,60 @@
+//===--- timeout linux implementation (type-only) ---------------*- 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_BASIC_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_BASIC_H
+
+#include "hdr/time_macros.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/CPP/expected.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 const 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};
+  }
+  // The implementation of this function is separated to timeout.h.
+  // This function pulls in the dependency to clock_conversion.h,
+  // which may transitively depend on vDSO hence futex. However, this structure
+  // would be passed to futex, so we need to avoid cyclic dependencies.
+  LIBC_INLINE void ensure_monotonic();
+};
+} // namespace internal
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_BASIC_H

>From fd3aceaa59985318105fbb1ecf084919917ef043 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Mon, 13 May 2024 14:26:59 -0400
Subject: [PATCH 5/5] address CR

---
 .../__support/threads/linux/CMakeLists.txt    |  2 +-
 .../src/__support/threads/linux/futex_utils.h |  2 +-
 libc/src/__support/time/linux/CMakeLists.txt  | 13 +++---
 .../linux/{timeout_basic.h => abs_timeout.h}  | 19 ++------
 libc/src/__support/time/linux/monotonicity.h  | 43 +++++++++++++++++++
 libc/src/__support/time/linux/timeout.h       | 36 ----------------
 .../src/__support/time/linux/CMakeLists.txt   |  3 +-
 .../src/__support/time/linux/timeout_test.cpp |  7 +--
 8 files changed, 62 insertions(+), 63 deletions(-)
 rename libc/src/__support/time/linux/{timeout_basic.h => abs_timeout.h} (64%)
 create mode 100644 libc/src/__support/time/linux/monotonicity.h
 delete mode 100644 libc/src/__support/time/linux/timeout.h

diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index eec1421629b22..d3353f6b3ff8c 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -19,7 +19,7 @@ add_header_library(
     libc.src.__support.CPP.atomic
     libc.src.__support.CPP.limits
     libc.src.__support.CPP.optional
-    libc.src.__support.time.linux.timeout_basic
+    libc.src.__support.time.linux.abs_timeout
 )
 
 add_header_library(
diff --git a/libc/src/__support/threads/linux/futex_utils.h b/libc/src/__support/threads/linux/futex_utils.h
index 45c725dc008f4..e40ade8e709b1 100644
--- a/libc/src/__support/threads/linux/futex_utils.h
+++ b/libc/src/__support/threads/linux/futex_utils.h
@@ -15,7 +15,7 @@
 #include "src/__support/OSUtil/syscall.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/threads/linux/futex_word.h"
-#include "src/__support/time/linux/timeout_basic.h"
+#include "src/__support/time/linux/abs_timeout.h"
 #include <linux/errno.h>
 #include <linux/futex.h>
 
diff --git a/libc/src/__support/time/linux/CMakeLists.txt b/libc/src/__support/time/linux/CMakeLists.txt
index 25aa48f3b6f36..dc1133f2f65b9 100644
--- a/libc/src/__support/time/linux/CMakeLists.txt
+++ b/libc/src/__support/time/linux/CMakeLists.txt
@@ -21,21 +21,22 @@ add_header_library(
 )
 
 add_header_library(
-  timeout_basic
+  abs_timeout
   HDRS
-    timeout_basic.h
+    abs_timeout.h
   DEPENDS
     libc.hdr.types.struct_timespec
     libc.src.__support.time.units
+    libc.src.__support.CPP.expected
 )
 
 add_header_library(
-  timeout
+  monotonicity
   HDRS
-    timeout.h
+    monotonicity.h
   DEPENDS
     .clock_gettime
     .clock_conversion
-    .timeout_basic
-    libc.src.__support.CPP.expected
+    .abs_timeout
+    libc.hdr.time_macros
 )
diff --git a/libc/src/__support/time/linux/timeout_basic.h b/libc/src/__support/time/linux/abs_timeout.h
similarity index 64%
rename from libc/src/__support/time/linux/timeout_basic.h
rename to libc/src/__support/time/linux/abs_timeout.h
index 68d3456366aa3..6e5e59b32b7ad 100644
--- a/libc/src/__support/time/linux/timeout_basic.h
+++ b/libc/src/__support/time/linux/abs_timeout.h
@@ -1,4 +1,4 @@
-//===--- timeout linux implementation (type-only) ---------------*- C++ -*-===//
+//===--- Linux absolute timeout ---------------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_BASIC_H
-#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_BASIC_H
+#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
 
 #include "hdr/time_macros.h"
 #include "hdr/types/struct_timespec.h"
@@ -19,12 +19,6 @@ 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;
@@ -48,13 +42,8 @@ class AbsTimeout {
 
     return AbsTimeout{ts, realtime};
   }
-  // The implementation of this function is separated to timeout.h.
-  // This function pulls in the dependency to clock_conversion.h,
-  // which may transitively depend on vDSO hence futex. However, this structure
-  // would be passed to futex, so we need to avoid cyclic dependencies.
-  LIBC_INLINE void ensure_monotonic();
 };
 } // namespace internal
 } // namespace LIBC_NAMESPACE
 
-#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_TIMEOUT_BASIC_H
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
diff --git a/libc/src/__support/time/linux/monotonicity.h b/libc/src/__support/time/linux/monotonicity.h
new file mode 100644
index 0000000000000..e413275430dd1
--- /dev/null
+++ b/libc/src/__support/time/linux/monotonicity.h
@@ -0,0 +1,43 @@
+//===--- 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_MONOTONICITY_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_MONOTONICITY_H
+
+#include "hdr/time_macros.h"
+#include "src/__support/libc_assert.h"
+#include "src/__support/time/linux/abs_timeout.h"
+#include "src/__support/time/linux/clock_conversion.h"
+namespace LIBC_NAMESPACE {
+namespace internal {
+// This function is separated from abs_timeout.
+// This function pulls in the dependency to clock_conversion.h,
+// which may transitively depend on vDSO hence futex. However, this structure
+// would be passed to futex, so we need to avoid cyclic dependencies.
+// This function 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.
+LIBC_INLINE void ensure_monotonicity(AbsTimeout &timeout) {
+  if (timeout.is_realtime()) {
+    auto res = AbsTimeout::from_timespec(
+        convert_clock(timeout.get_timespec(), CLOCK_REALTIME, CLOCK_MONOTONIC),
+        false);
+
+    LIBC_ASSERT(res.has_value());
+    if (!res.has_value())
+      __builtin_unreachable();
+
+    timeout = *res;
+  }
+}
+} // namespace internal
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_MONOTONICITY_H
diff --git a/libc/src/__support/time/linux/timeout.h b/libc/src/__support/time/linux/timeout.h
deleted file mode 100644
index f5f3e8dc65771..0000000000000
--- a/libc/src/__support/time/linux/timeout.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//===--- 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 "src/__support/time/linux/clock_conversion.h"
-#include "src/__support/time/linux/timeout_basic.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.
-LIBC_INLINE void AbsTimeout::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/time/linux/CMakeLists.txt b/libc/test/src/__support/time/linux/CMakeLists.txt
index 8c95667824a9c..3174986283dd8 100644
--- a/libc/test/src/__support/time/linux/CMakeLists.txt
+++ b/libc/test/src/__support/time/linux/CMakeLists.txt
@@ -3,6 +3,7 @@ add_libc_test(
   SUITE libc-support-time-tests
   SRCS timeout_test.cpp
   DEPENDS
-    libc.src.__support.time.linux.timeout
+    libc.src.__support.time.linux.abs_timeout
+    libc.src.__support.time.linux.monotonicity
     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
index 8c638dd7bb777..711dc0d3c944f 100644
--- a/libc/test/src/__support/time/linux/timeout_test.cpp
+++ b/libc/test/src/__support/time/linux/timeout_test.cpp
@@ -7,7 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/__support/CPP/expected.h"
-#include "src/__support/time/linux/timeout.h"
+#include "src/__support/time/linux/abs_timeout.h"
+#include "src/__support/time/linux/monotonicity.h"
 #include "test/UnitTest/Test.h"
 
 namespace LIBC_NAMESPACE {
@@ -39,7 +40,7 @@ TEST(LlvmLibcSupportLinuxTimeoutTest, NoChangeIfClockIsMonotonic) {
   cpp::expected<AbsTimeout, AbsTimeout::Error> result =
       AbsTimeout::from_timespec(ts, false);
   ASSERT_TRUE(result.has_value());
-  result->ensure_monotonic();
+  ensure_monotonicity(*result);
   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));
@@ -50,7 +51,7 @@ TEST(LlvmLibcSupportLinuxTimeoutTest, ValidAfterConversion) {
   cpp::expected<AbsTimeout, AbsTimeout::Error> result =
       AbsTimeout::from_timespec(ts, true);
   ASSERT_TRUE(result.has_value());
-  result->ensure_monotonic();
+  ensure_monotonicity(*result);
   ASSERT_FALSE(result->is_realtime());
   ASSERT_TRUE(
       AbsTimeout::from_timespec(result->get_timespec(), false).has_value());



More information about the libc-commits mailing list