[libc-commits] [libc] [libc] Make the sys/time.h timer macros single statements (PR #236519)

Mike Lothian via libc-commits libc-commits at lists.llvm.org
Thu Oct 29 12:10:24 PDT 2026


https://github.com/FireBurn updated https://github.com/llvm/llvm-project/pull/236519

>From fa948e1d6b3def5b76536512069439ae9505c3dd Mon Sep 17 00:00:00 2001
From: Mike Lothian <mike at fireburn.co.uk>
Date: Wed, 28 Oct 2026 17:23:34 +0000
Subject: [PATCH] [libc] Make the sys/time.h timer macros single statements

timerisset ended in a semicolon, so the only way it is ever used, as the condition of an if, did not compile. timerclear, timeradd and timersub were each more than one statement with nothing holding them together, so a caller writing "if (c) timeradd(...); else" silently took only the first line of the macro, or did not compile

Make timerisset and timerclear single expressions and wrap timeradd and timersub in do while so each is one statement. The arithmetic is unchanged. Add tests for the results and for using each macro in an if and else

AI disclosure: this patch was written with Claude Code. I reviewed the change and ran the new test; it fails to build without the fix and passes with it

Assisted-by: Claude
---
 .../llvm-libc-macros/linux/sys-time-macros.h  | 58 ++++++------
 libc/test/include/CMakeLists.txt              | 15 ++++
 libc/test/include/sys_time_macros_test.cpp    | 89 +++++++++++++++++++
 3 files changed, 137 insertions(+), 25 deletions(-)
 create mode 100644 libc/test/include/sys_time_macros_test.cpp

diff --git a/libc/include/llvm-libc-macros/linux/sys-time-macros.h b/libc/include/llvm-libc-macros/linux/sys-time-macros.h
index 677eab50ba0989..4179fc4ad6794f 100644
--- a/libc/include/llvm-libc-macros/linux/sys-time-macros.h
+++ b/libc/include/llvm-libc-macros/linux/sys-time-macros.h
@@ -1,10 +1,15 @@
-//===-- Definition of macros from sys/time.h ------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // 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
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of macros from sys/time.h.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_MACROS_LINUX_SYS_TIME_MACROS_H
 #define LLVM_LIBC_MACROS_LINUX_SYS_TIME_MACROS_H
@@ -18,36 +23,39 @@
 // usec value is greater than 999,999 then the microseconds are turned into full
 // seconds (1,000,000 is subtracted from usec and 1 is added to sec).
 #define timeradd(timeval_ptr_a, timeval_ptr_b, timeval_ptr_result)             \
-  (timeval_ptr_result)->tv_sec =                                               \
-      (timeval_ptr_a)->tv_sec + (timeval_ptr_b)->tv_sec +                      \
-      (((timeval_ptr_a)->tv_usec + (timeval_ptr_b)->tv_usec) >= 1000000 ? 1    \
-                                                                        : 0);  \
-  (timeval_ptr_result)->tv_usec =                                              \
-      (timeval_ptr_a)->tv_usec + (timeval_ptr_b)->tv_usec -                    \
-      (((timeval_ptr_a)->tv_usec + (timeval_ptr_b)->tv_usec) >= 1000000        \
-           ? 1000000                                                           \
-           : 0);
-
-// Subtract two timevals and put the result in timeval_ptr_result. If the
-// resulting usec value is less than 0 then 1,000,000 is added to usec and 1 is
-// subtracted from sec.
+  do {                                                                         \
+    (timeval_ptr_result)->tv_sec =                                             \
+        (timeval_ptr_a)->tv_sec + (timeval_ptr_b)->tv_sec;                     \
+    (timeval_ptr_result)->tv_usec =                                            \
+        (timeval_ptr_a)->tv_usec + (timeval_ptr_b)->tv_usec;                   \
+    if ((timeval_ptr_result)->tv_usec >= 1000000) {                            \
+      ++(timeval_ptr_result)->tv_sec;                                          \
+      (timeval_ptr_result)->tv_usec -= 1000000;                                \
+    }                                                                          \
+  } while (0)
+
+// Subtract timeval_ptr_b from timeval_ptr_a and put the result in
+// timeval_ptr_result. If the resulting usec value is less than 0 then 1,000,000
+// is added to usec and 1 is subtracted from sec.
 #define timersub(timeval_ptr_a, timeval_ptr_b, timeval_ptr_result)             \
-  (timeval_ptr_result)->tv_sec =                                               \
-      (timeval_ptr_a)->tv_sec - (timeval_ptr_b)->tv_sec -                      \
-      (((timeval_ptr_a)->tv_usec - (timeval_ptr_b)->tv_usec) < 0 ? 1 : 0);     \
-  (timeval_ptr_result)->tv_usec =                                              \
-      (timeval_ptr_a)->tv_usec - (timeval_ptr_b)->tv_usec +                    \
-      (((timeval_ptr_a)->tv_usec - (timeval_ptr_b)->tv_usec) < 0 ? 1000000     \
-                                                                 : 0);
+  do {                                                                         \
+    (timeval_ptr_result)->tv_sec =                                             \
+        (timeval_ptr_a)->tv_sec - (timeval_ptr_b)->tv_sec;                     \
+    (timeval_ptr_result)->tv_usec =                                            \
+        (timeval_ptr_a)->tv_usec - (timeval_ptr_b)->tv_usec;                   \
+    if ((timeval_ptr_result)->tv_usec < 0) {                                   \
+      --(timeval_ptr_result)->tv_sec;                                          \
+      (timeval_ptr_result)->tv_usec += 1000000;                                \
+    }                                                                          \
+  } while (0)
 
 // Reset a timeval to the epoch.
 #define timerclear(timeval_ptr)                                                \
-  (timeval_ptr)->tv_sec = 0;                                                   \
-  (timeval_ptr)->tv_usec = 0;
+  ((timeval_ptr)->tv_sec = (timeval_ptr)->tv_usec = 0)
 
-// Determine if a timeval is set to the epoch.
+// Determine if a timeval is set to something other than the epoch.
 #define timerisset(timeval_ptr)                                                \
-  (timeval_ptr)->tv_sec != 0 || (timeval_ptr)->tv_usec != 0;
+  ((timeval_ptr)->tv_sec != 0 || (timeval_ptr)->tv_usec != 0)
 
 // Compare two timevals using CMP.
 #define timercmp(timeval_ptr_a, timeval_ptr_b, CMP)                            \
diff --git a/libc/test/include/CMakeLists.txt b/libc/test/include/CMakeLists.txt
index fb52c23f64bffb..28fdff096bbcea 100644
--- a/libc/test/include/CMakeLists.txt
+++ b/libc/test/include/CMakeLists.txt
@@ -54,6 +54,21 @@ add_libc_test(
     -Wno-gnu-statement-expression-from-macro-expansion
 )
 
+if(LIBC_TARGET_OS_IS_LINUX)
+  add_libc_test(
+    sys_time_macros_test
+    SUITE
+      libc-include-tests
+    SRCS
+      sys_time_macros_test.cpp
+    DEPENDS
+      libc.include.llvm-libc-macros.sys_time_macros
+      libc.include.llvm-libc-types.struct_timeval
+      libc.include.llvm-libc-types.suseconds_t
+      libc.include.llvm-libc-types.time_t
+  )
+endif()
+
 add_libc_test(
   stdbit_test
   SUITE
diff --git a/libc/test/include/sys_time_macros_test.cpp b/libc/test/include/sys_time_macros_test.cpp
new file mode 100644
index 00000000000000..aa24b7b85ceffc
--- /dev/null
+++ b/libc/test/include/sys_time_macros_test.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for the timeval macros in sys-time-macros.h.
+///
+//===----------------------------------------------------------------------===//
+
+#include "include/llvm-libc-macros/sys-time-macros.h"
+#include "include/llvm-libc-types/struct_timeval.h"
+#include "include/llvm-libc-types/suseconds_t.h"
+#include "include/llvm-libc-types/time_t.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcTimerMacrosTest, TimerAdd) {
+  struct timeval a = {1, 600000};
+  struct timeval b = {2, 700000};
+  struct timeval result = {};
+  timeradd(&a, &b, &result);
+  EXPECT_EQ(result.tv_sec, time_t(4));
+  EXPECT_EQ(result.tv_usec, suseconds_t(300000));
+
+  // Without a carry.
+  b = {2, 100000};
+  timeradd(&a, &b, &result);
+  EXPECT_EQ(result.tv_sec, time_t(3));
+  EXPECT_EQ(result.tv_usec, suseconds_t(700000));
+}
+
+TEST(LlvmLibcTimerMacrosTest, TimerSub) {
+  struct timeval a = {5, 100000};
+  struct timeval b = {2, 300000};
+  struct timeval result = {};
+  timersub(&a, &b, &result);
+  EXPECT_EQ(result.tv_sec, time_t(2));
+  EXPECT_EQ(result.tv_usec, suseconds_t(800000));
+
+  // Without a borrow.
+  b = {2, 50000};
+  timersub(&a, &b, &result);
+  EXPECT_EQ(result.tv_sec, time_t(3));
+  EXPECT_EQ(result.tv_usec, suseconds_t(50000));
+}
+
+TEST(LlvmLibcTimerMacrosTest, TimerClearAndIsSet) {
+  struct timeval t = {3, 4};
+  EXPECT_TRUE(timerisset(&t));
+  timerclear(&t);
+  EXPECT_EQ(t.tv_sec, time_t(0));
+  EXPECT_EQ(t.tv_usec, suseconds_t(0));
+  EXPECT_FALSE(timerisset(&t));
+
+  t = {0, 1};
+  EXPECT_TRUE(timerisset(&t));
+}
+
+// Each macro has to be usable as a single statement, or the else below
+// attaches to the wrong if or does not compile.
+TEST(LlvmLibcTimerMacrosTest, UsableAsOneStatement) {
+  struct timeval a = {1, 0};
+  struct timeval b = {1, 0};
+  struct timeval result = {};
+  int taken = 0;
+
+  if (timerisset(&a))
+    timeradd(&a, &b, &result);
+  else
+    taken = 1;
+  EXPECT_EQ(result.tv_sec, time_t(2));
+  EXPECT_EQ(taken, 0);
+
+  if (!timerisset(&a))
+    taken = 1;
+  else
+    timersub(&a, &b, &result);
+  EXPECT_EQ(result.tv_sec, time_t(0));
+  EXPECT_EQ(taken, 0);
+
+  if (taken)
+    taken = 1;
+  else
+    timerclear(&result);
+  EXPECT_FALSE(timerisset(&result));
+}



More information about the libc-commits mailing list