[libc-commits] [libc] [libc] Implement ppoll in poll (PR #225767)

Aman Maurya via libc-commits libc-commits at lists.llvm.org
Thu Sep 24 02:55:40 PDT 2026


https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/225767

>From e2d847bbcbd37c45d55aa459705688004c5d2689 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 23 Sep 2026 18:38:40 +0530
Subject: [PATCH 1/4] [libc] Implement ppoll in poll

Assisted by Antigravity and Gemini.
---
 libc/config/linux/aarch64/entrypoints.txt |  1 +
 libc/config/linux/riscv/entrypoints.txt   |  1 +
 libc/config/linux/x86_64/entrypoints.txt  |  1 +
 libc/hdr/CMakeLists.txt                   |  8 +++
 libc/hdr/poll_macros.h                    | 22 ++++++
 libc/include/CMakeLists.txt               |  2 +
 libc/include/poll.yaml                    | 11 +++
 libc/src/poll/CMakeLists.txt              |  7 ++
 libc/src/poll/linux/CMakeLists.txt        | 17 +++++
 libc/src/poll/linux/ppoll.cpp             | 57 +++++++++++++++
 libc/src/poll/ppoll.h                     | 25 +++++++
 libc/test/src/poll/CMakeLists.txt         | 22 ++++++
 libc/test/src/poll/ppoll_test.cpp         | 86 +++++++++++++++++++++++
 13 files changed, 260 insertions(+)
 create mode 100644 libc/hdr/poll_macros.h
 create mode 100644 libc/src/poll/linux/ppoll.cpp
 create mode 100644 libc/src/poll/ppoll.h
 create mode 100644 libc/test/src/poll/ppoll_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 479a70846714f0..e4cfff496d59a6 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -57,6 +57,7 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # poll.h entrypoints
     libc.src.poll.poll
+    libc.src.poll.ppoll
 
     # pwd.h entrypoints
     libc.src.pwd.endpwent
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index c841b28d9e544e..7f1a82b77b395e 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -76,6 +76,7 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # poll.h entrypoints
     libc.src.poll.poll
+    libc.src.poll.ppoll
 
     # pwd.h entrypoints
     libc.src.pwd.endpwent
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 786a1ea0743055..7733a4fb1de48d 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -76,6 +76,7 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # poll.h entrypoints
     libc.src.poll.poll
+    libc.src.poll.ppoll
 
     # pwd.h entrypoints
     libc.src.pwd.endpwent
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index fa01bc55785daa..64efe4968b51ac 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -120,6 +120,14 @@ add_proxy_header_library(
     libc.include.llvm-libc-macros.netinet_in_macros
 )
 
+add_proxy_header_library(
+  poll_macros
+  HDRS
+    poll_macros.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-macros.poll-macros
+)
+
 add_proxy_header_library(
   pthread_macros
   HDRS
diff --git a/libc/hdr/poll_macros.h b/libc/hdr/poll_macros.h
new file mode 100644
index 00000000000000..f24d93956ca231
--- /dev/null
+++ b/libc/hdr/poll_macros.h
@@ -0,0 +1,22 @@
+//===-- Definition of macros from poll.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_POLL_MACROS_H
+#define LLVM_LIBC_HDR_POLL_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/poll-macros.h"
+
+#else // Overlay mode
+
+#include <poll.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_POLL_MACROS_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index f63e7e3dc6a734..e46f8c163ef2d2 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -1257,6 +1257,8 @@ add_header_macro(
   DEPENDS
     .llvm-libc-types.struct_pollfd
     .llvm-libc-types.nfds_t
+    .llvm-libc-types.struct_timespec
+    .llvm-libc-types.sigset_t
     .llvm-libc-macros.poll-macros
 )
 
diff --git a/libc/include/poll.yaml b/libc/include/poll.yaml
index bb7d99e87aa078..085445da1304f0 100644
--- a/libc/include/poll.yaml
+++ b/libc/include/poll.yaml
@@ -25,6 +25,8 @@ macros:
 types:
   - type_name: struct_pollfd
   - type_name: nfds_t
+  - type_name: struct_timespec
+  - type_name: sigset_t
 enums: []
 functions:
   - name: poll
@@ -35,3 +37,12 @@ functions:
       - type: struct pollfd *
       - type: nfds_t
       - type: int
+  - name: ppoll
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: struct pollfd *
+      - type: nfds_t
+      - type: const struct timespec *
+      - type: const sigset_t *
diff --git a/libc/src/poll/CMakeLists.txt b/libc/src/poll/CMakeLists.txt
index 65fbe46bb46128..c5bd970bb9a74e 100644
--- a/libc/src/poll/CMakeLists.txt
+++ b/libc/src/poll/CMakeLists.txt
@@ -8,3 +8,10 @@ add_entrypoint_object(
   DEPENDS
     .${LIBC_TARGET_OS}.poll
 )
+
+add_entrypoint_object(
+  ppoll
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.ppoll
+)
diff --git a/libc/src/poll/linux/CMakeLists.txt b/libc/src/poll/linux/CMakeLists.txt
index a9ef596c36c893..12871edf630ec4 100644
--- a/libc/src/poll/linux/CMakeLists.txt
+++ b/libc/src/poll/linux/CMakeLists.txt
@@ -12,3 +12,20 @@ add_entrypoint_object(
     libc.src.__support.OSUtil.osutil
     libc.src.errno.errno
 )
+
+add_entrypoint_object(
+  ppoll
+  SRCS
+    ppoll.cpp
+  HDRS
+    ../ppoll.h
+  DEPENDS
+    libc.hdr.signal_macros
+    libc.hdr.types.nfds_t
+    libc.hdr.types.sigset_t
+    libc.hdr.types.struct_pollfd
+    libc.hdr.types.struct_timespec
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
diff --git a/libc/src/poll/linux/ppoll.cpp b/libc/src/poll/linux/ppoll.cpp
new file mode 100644
index 00000000000000..245ba4acd41e51
--- /dev/null
+++ b/libc/src/poll/linux/ppoll.cpp
@@ -0,0 +1,57 @@
+//===-- Linux implementation of ppoll ------------------------------------===//
+//
+// 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/poll/ppoll.h"
+
+#include "hdr/signal_macros.h"
+#include "hdr/types/nfds_t.h"
+#include "hdr/types/sigset_t.h"
+#include "hdr/types/struct_pollfd.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/OSUtil/syscall.h" // syscall_impl
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+#include <sys/syscall.h> // SYS_ppoll, SYS_ppoll_time64
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, ppoll,
+                   (struct pollfd * fds, nfds_t nfds,
+                    const struct timespec *tmo_p, const sigset_t *sigmask)) {
+  timespec ts;
+  timespec *tsp = nullptr;
+  if (tmo_p != nullptr) {
+    ts = *tmo_p;
+    tsp = &ts;
+  }
+
+#if defined(SYS_ppoll_time64)
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll_time64, fds, nfds, tsp,
+                                              sigmask, NSIG / 8);
+#elif defined(SYS_ppoll)
+  static_assert(
+      sizeof(timespec::tv_nsec) == sizeof(long),
+      "This legacy syscall fallback is only safe on platforms where tv_nsec "
+      "matches the register size (long). It is unsafe on 32-bit platforms "
+      "with 64-bit tv_nsec.");
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll, fds, nfds, tsp,
+                                              sigmask, NSIG / 8);
+#else
+#error "ppoll and ppoll_time64 syscalls not available."
+#endif
+
+  if (ret < 0) {
+    libc_errno = -ret;
+    return -1;
+  }
+  return ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/poll/ppoll.h b/libc/src/poll/ppoll.h
new file mode 100644
index 00000000000000..71b90e4ac69c64
--- /dev/null
+++ b/libc/src/poll/ppoll.h
@@ -0,0 +1,25 @@
+//===-- Implementation header for ppoll -------------------------*- 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_POLL_PPOLL_H
+#define LLVM_LIBC_SRC_POLL_PPOLL_H
+
+#include "hdr/types/nfds_t.h"
+#include "hdr/types/sigset_t.h"
+#include "hdr/types/struct_pollfd.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p,
+          const sigset_t *sigmask);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_POLL_PPOLL_H
diff --git a/libc/test/src/poll/CMakeLists.txt b/libc/test/src/poll/CMakeLists.txt
index b6f18c5dbf4bfb..5097f05bbddb90 100644
--- a/libc/test/src/poll/CMakeLists.txt
+++ b/libc/test/src/poll/CMakeLists.txt
@@ -12,3 +12,25 @@ add_libc_test(
     libc.src.poll.poll
     libc.test.UnitTest.ErrnoCheckingTest
 )
+
+add_libc_test(
+  ppoll_test
+  SUITE
+    libc_poll_unittests
+  SRCS
+    ppoll_test.cpp
+  DEPENDS
+    libc.hdr.limits_macros
+    libc.hdr.poll_macros
+    libc.hdr.types.sigset_t
+    libc.hdr.types.struct_pollfd
+    libc.hdr.types.struct_timespec
+    libc.src.__support.CPP.scope
+    libc.src.errno.errno
+    libc.src.poll.ppoll
+    libc.src.unistd.close
+    libc.src.unistd.pipe
+    libc.src.unistd.read
+    libc.src.unistd.write
+    libc.test.UnitTest.ErrnoCheckingTest
+)
diff --git a/libc/test/src/poll/ppoll_test.cpp b/libc/test/src/poll/ppoll_test.cpp
new file mode 100644
index 00000000000000..f6fe9941645866
--- /dev/null
+++ b/libc/test/src/poll/ppoll_test.cpp
@@ -0,0 +1,86 @@
+//===-- Unittests for ppoll -----------------------------------------------===//
+//
+// 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/limits_macros.h"
+#include "hdr/poll_macros.h"
+#include "hdr/types/sigset_t.h"
+#include "hdr/types/struct_pollfd.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/CPP/scope.h"
+#include "src/poll/ppoll.h"
+#include "src/unistd/close.h"
+#include "src/unistd/pipe.h"
+#include "src/unistd/read.h"
+#include "src/unistd/write.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcPPollTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcPPollTest, SmokeTest) {
+  timespec ts{0, 0};
+  int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, nullptr);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_EQ(0, ret);
+}
+
+TEST_F(LlvmLibcPPollTest, SmokeFailureTest) {
+  int ret = LIBC_NAMESPACE::ppoll(nullptr, UINT_MAX, nullptr, nullptr);
+  ASSERT_ERRNO_EQ(EINVAL);
+  ASSERT_EQ(-1, ret);
+}
+
+TEST_F(LlvmLibcPPollTest, TimeoutNotMutated) {
+  const timespec orig_ts{0, 0};
+  timespec ts = orig_ts;
+  int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, nullptr);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_EQ(0, ret);
+  ASSERT_EQ(ts.tv_sec, orig_ts.tv_sec);
+  ASSERT_EQ(ts.tv_nsec, orig_ts.tv_nsec);
+}
+
+TEST_F(LlvmLibcPPollTest, WithSigmask) {
+  timespec ts{0, 0};
+  sigset_t mask{};
+  int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, &mask);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_EQ(0, ret);
+}
+
+TEST_F(LlvmLibcPPollTest, PipeReadiness) {
+  int pipefd[2];
+  ASSERT_EQ(LIBC_NAMESPACE::pipe(pipefd), 0);
+  ASSERT_ERRNO_SUCCESS();
+
+  LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+    LIBC_NAMESPACE::close(pipefd[0]);
+    LIBC_NAMESPACE::close(pipefd[1]);
+  });
+
+  pollfd pfd{pipefd[0], POLLIN, 0};
+  timespec ts{0, 0};
+  int ret = LIBC_NAMESPACE::ppoll(&pfd, 1, &ts, nullptr);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_EQ(0, ret);
+  ASSERT_EQ(0, static_cast<int>(pfd.revents));
+
+  char c = 'x';
+  ASSERT_EQ(LIBC_NAMESPACE::write(pipefd[1], &c, 1), static_cast<ssize_t>(1));
+  ASSERT_ERRNO_SUCCESS();
+
+  ret = LIBC_NAMESPACE::ppoll(&pfd, 1, &ts, nullptr);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_EQ(1, ret);
+  ASSERT_EQ(POLLIN, pfd.revents & POLLIN);
+
+  char buf = 0;
+  ASSERT_EQ(LIBC_NAMESPACE::read(pipefd[0], &buf, 1), static_cast<ssize_t>(1));
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_EQ('x', buf);
+}

>From 773e7af60805318a0a014fec1b1d0cce07ecdf8b Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 23 Sep 2026 21:43:40 +0530
Subject: [PATCH 2/4] [libc] Address review: update file headers and add NSIG/8
 comment

- Update file headers in all new files to the current LLVM coding standard style (no description in first === line, add /// \file doxygen block).

- Add comment explaining NSIG / 8 in ppoll syscall invocation.
---
 libc/hdr/poll_macros.h            | 7 ++++++-
 libc/src/poll/linux/ppoll.cpp     | 9 ++++++++-
 libc/src/poll/ppoll.h             | 7 ++++++-
 libc/test/src/poll/ppoll_test.cpp | 7 ++++++-
 4 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/libc/hdr/poll_macros.h b/libc/hdr/poll_macros.h
index f24d93956ca231..1567b6d8b24834 100644
--- a/libc/hdr/poll_macros.h
+++ b/libc/hdr/poll_macros.h
@@ -1,10 +1,15 @@
-//===-- Definition of macros from poll.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
+/// Proxy header for poll.h macros.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_HDR_POLL_MACROS_H
 #define LLVM_LIBC_HDR_POLL_MACROS_H
diff --git a/libc/src/poll/linux/ppoll.cpp b/libc/src/poll/linux/ppoll.cpp
index 245ba4acd41e51..f924e1b04df4fc 100644
--- a/libc/src/poll/linux/ppoll.cpp
+++ b/libc/src/poll/linux/ppoll.cpp
@@ -1,10 +1,15 @@
-//===-- Linux implementation of ppoll ------------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // 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
+/// Linux implementation of ppoll.
+///
+//===----------------------------------------------------------------------===//
 
 #include "src/poll/ppoll.h"
 
@@ -33,6 +38,8 @@ LLVM_LIBC_FUNCTION(int, ppoll,
   }
 
 #if defined(SYS_ppoll_time64)
+  // The kernel expects the signal mask size in bytes, not the number of
+  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
   int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll_time64, fds, nfds, tsp,
                                               sigmask, NSIG / 8);
 #elif defined(SYS_ppoll)
diff --git a/libc/src/poll/ppoll.h b/libc/src/poll/ppoll.h
index 71b90e4ac69c64..72524a24edbb50 100644
--- a/libc/src/poll/ppoll.h
+++ b/libc/src/poll/ppoll.h
@@ -1,10 +1,15 @@
-//===-- Implementation header for ppoll -------------------------*- 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
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for ppoll.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC_POLL_PPOLL_H
 #define LLVM_LIBC_SRC_POLL_PPOLL_H
diff --git a/libc/test/src/poll/ppoll_test.cpp b/libc/test/src/poll/ppoll_test.cpp
index f6fe9941645866..c6ad70e9209879 100644
--- a/libc/test/src/poll/ppoll_test.cpp
+++ b/libc/test/src/poll/ppoll_test.cpp
@@ -1,10 +1,15 @@
-//===-- Unittests for ppoll -----------------------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // 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
+/// Unittests for ppoll.
+///
+//===----------------------------------------------------------------------===//
 
 #include "hdr/limits_macros.h"
 #include "hdr/poll_macros.h"

>From b1591ba46941c6f8d8a30c5c9f5522e435bb5850 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Thu, 24 Sep 2026 09:59:29 +0530
Subject: [PATCH 3/4] [libc] Address review: move ppoll to syscall wrapper and
 add restrict

Assisted by Gemini.
---
 libc/include/poll.yaml                        |  4 +-
 .../linux/syscall_wrappers/CMakeLists.txt     | 17 ++++++
 .../OSUtil/linux/syscall_wrappers/ppoll.h     | 59 +++++++++++++++++++
 libc/src/poll/linux/CMakeLists.txt            |  7 ++-
 libc/src/poll/linux/ppoll.cpp                 | 44 +++-----------
 libc/src/poll/ppoll.h                         |  5 +-
 6 files changed, 92 insertions(+), 44 deletions(-)
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h

diff --git a/libc/include/poll.yaml b/libc/include/poll.yaml
index 085445da1304f0..1bf1a4e2448a8a 100644
--- a/libc/include/poll.yaml
+++ b/libc/include/poll.yaml
@@ -44,5 +44,5 @@ functions:
     arguments:
       - type: struct pollfd *
       - type: nfds_t
-      - type: const struct timespec *
-      - type: const sigset_t *
+      - type: const struct timespec *__restrict
+      - type: const sigset_t *__restrict
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index d1fbfa37715a9f..aebec048c8f273 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -1137,6 +1137,23 @@ add_header_library(
     libc.include.sys_syscall
 )
 
+add_header_library(
+  ppoll
+  HDRS
+    ppoll.h
+  DEPENDS
+    libc.hdr.signal_macros
+    libc.hdr.types.nfds_t
+    libc.hdr.types.sigset_t
+    libc.hdr.types.struct_pollfd
+    libc.hdr.types.struct_timespec
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
 add_header_library(
   rt_sigaction
   HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h
new file mode 100644
index 00000000000000..77846c74d8eeff
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Syscall wrapper for ppoll.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_PPOLL_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_PPOLL_H
+
+#include "hdr/signal_macros.h"
+#include "hdr/types/nfds_t.h"
+#include "hdr/types/sigset_t.h"
+#include "hdr/types/struct_pollfd.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> ppoll(struct pollfd *fds, nfds_t nfds,
+                               const struct timespec *__restrict tmo_p,
+                               const sigset_t *__restrict sigmask) {
+#if defined(SYS_ppoll_time64)
+  static_assert(
+      sizeof(time_t) == sizeof(int64_t),
+      "SYS_ppoll_time64 requires struct timespec with 64-bit members.");
+  // The kernel expects the signal mask size in bytes, not the number of
+  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
+  return syscall_checked<int>(SYS_ppoll_time64, fds, nfds, tmo_p, sigmask,
+                              NSIG / 8);
+#elif defined(SYS_ppoll)
+  static_assert(
+      sizeof(timespec::tv_nsec) == sizeof(long),
+      "This legacy syscall fallback is only safe on platforms where tv_nsec "
+      "matches the register size (long). It is unsafe on 32-bit platforms "
+      "with 64-bit tv_nsec.");
+  // The kernel expects the signal mask size in bytes, not the number of
+  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
+  return syscall_checked<int>(SYS_ppoll, fds, nfds, tmo_p, sigmask, NSIG / 8);
+#else
+#error "ppoll and ppoll_time64 syscalls not available."
+#endif
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_PPOLL_H
diff --git a/libc/src/poll/linux/CMakeLists.txt b/libc/src/poll/linux/CMakeLists.txt
index 12871edf630ec4..eaee546086e45d 100644
--- a/libc/src/poll/linux/CMakeLists.txt
+++ b/libc/src/poll/linux/CMakeLists.txt
@@ -20,12 +20,13 @@ add_entrypoint_object(
   HDRS
     ../ppoll.h
   DEPENDS
-    libc.hdr.signal_macros
     libc.hdr.types.nfds_t
     libc.hdr.types.sigset_t
     libc.hdr.types.struct_pollfd
     libc.hdr.types.struct_timespec
-    libc.include.sys_syscall
-    libc.src.__support.OSUtil.osutil
+    libc.src.__support.OSUtil.linux.syscall_wrappers.ppoll
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
     libc.src.errno.errno
 )
diff --git a/libc/src/poll/linux/ppoll.cpp b/libc/src/poll/linux/ppoll.cpp
index f924e1b04df4fc..713e4d497fcb7e 100644
--- a/libc/src/poll/linux/ppoll.cpp
+++ b/libc/src/poll/linux/ppoll.cpp
@@ -12,53 +12,23 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/poll/ppoll.h"
-
-#include "hdr/signal_macros.h"
-#include "hdr/types/nfds_t.h"
-#include "hdr/types/sigset_t.h"
-#include "hdr/types/struct_pollfd.h"
-#include "hdr/types/struct_timespec.h"
-#include "src/__support/OSUtil/syscall.h" // syscall_impl
+#include "src/__support/OSUtil/linux/syscall_wrappers/ppoll.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
-#include <sys/syscall.h> // SYS_ppoll, SYS_ppoll_time64
-
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, ppoll,
                    (struct pollfd * fds, nfds_t nfds,
-                    const struct timespec *tmo_p, const sigset_t *sigmask)) {
-  timespec ts;
-  timespec *tsp = nullptr;
-  if (tmo_p != nullptr) {
-    ts = *tmo_p;
-    tsp = &ts;
-  }
-
-#if defined(SYS_ppoll_time64)
-  // The kernel expects the signal mask size in bytes, not the number of
-  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
-  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll_time64, fds, nfds, tsp,
-                                              sigmask, NSIG / 8);
-#elif defined(SYS_ppoll)
-  static_assert(
-      sizeof(timespec::tv_nsec) == sizeof(long),
-      "This legacy syscall fallback is only safe on platforms where tv_nsec "
-      "matches the register size (long). It is unsafe on 32-bit platforms "
-      "with 64-bit tv_nsec.");
-  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll, fds, nfds, tsp,
-                                              sigmask, NSIG / 8);
-#else
-#error "ppoll and ppoll_time64 syscalls not available."
-#endif
-
-  if (ret < 0) {
-    libc_errno = -ret;
+                    const struct timespec *__restrict tmo_p,
+                    const sigset_t *__restrict sigmask)) {
+  auto result = linux_syscalls::ppoll(fds, nfds, tmo_p, sigmask);
+  if (!result) {
+    libc_errno = result.error();
     return -1;
   }
-  return ret;
+  return result.value();
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/poll/ppoll.h b/libc/src/poll/ppoll.h
index 72524a24edbb50..0886a332c3fad4 100644
--- a/libc/src/poll/ppoll.h
+++ b/libc/src/poll/ppoll.h
@@ -22,8 +22,9 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p,
-          const sigset_t *sigmask);
+int ppoll(struct pollfd *fds, nfds_t nfds,
+          const struct timespec *__restrict tmo_p,
+          const sigset_t *__restrict sigmask);
 
 } // namespace LIBC_NAMESPACE_DECL
 

>From 7681ac672dcff6b05e7a63ad0d8aad73bb6871e6 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Thu, 24 Sep 2026 15:24:35 +0530
Subject: [PATCH 4/4] [libc] Address review: copy timeout in ppoll entrypoint
 and strengthen tests

Assisted by Gemini.
---
 .../OSUtil/linux/syscall_wrappers/ppoll.h     | 19 +++--
 libc/src/poll/linux/ppoll.cpp                 |  9 +-
 libc/test/src/poll/CMakeLists.txt             | 10 +++
 libc/test/src/poll/ppoll_test.cpp             | 82 +++++++++++++++++--
 4 files changed, 104 insertions(+), 16 deletions(-)

diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h
index 77846c74d8eeff..6550f9408eca27 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/ppoll.h
@@ -9,6 +9,11 @@
 /// \file
 /// Syscall wrapper for ppoll.
 ///
+/// Note: On Linux, the raw ppoll syscall modifies its timeout argument to
+/// return the remaining time if interrupted. Therefore, this wrapper accepts
+/// a mutable timespec pointer. The POSIX ppoll entrypoint is responsible for
+/// making a copy to prevent mutating the user's const timeout argument.
+///
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_PPOLL_H
@@ -29,25 +34,25 @@ namespace LIBC_NAMESPACE_DECL {
 namespace linux_syscalls {
 
 LIBC_INLINE ErrorOr<int> ppoll(struct pollfd *fds, nfds_t nfds,
-                               const struct timespec *__restrict tmo_p,
+                               struct timespec *__restrict tmo_p,
                                const sigset_t *__restrict sigmask) {
+  // The kernel expects the signal mask size in bytes, not the number of
+  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
+  const size_t sigsetsize = NSIG / 8;
+
 #if defined(SYS_ppoll_time64)
   static_assert(
       sizeof(time_t) == sizeof(int64_t),
       "SYS_ppoll_time64 requires struct timespec with 64-bit members.");
-  // The kernel expects the signal mask size in bytes, not the number of
-  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
   return syscall_checked<int>(SYS_ppoll_time64, fds, nfds, tmo_p, sigmask,
-                              NSIG / 8);
+                              sigsetsize);
 #elif defined(SYS_ppoll)
   static_assert(
       sizeof(timespec::tv_nsec) == sizeof(long),
       "This legacy syscall fallback is only safe on platforms where tv_nsec "
       "matches the register size (long). It is unsafe on 32-bit platforms "
       "with 64-bit tv_nsec.");
-  // The kernel expects the signal mask size in bytes, not the number of
-  // signals. NSIG is the signal count, so NSIG / 8 gives the byte size.
-  return syscall_checked<int>(SYS_ppoll, fds, nfds, tmo_p, sigmask, NSIG / 8);
+  return syscall_checked<int>(SYS_ppoll, fds, nfds, tmo_p, sigmask, sigsetsize);
 #else
 #error "ppoll and ppoll_time64 syscalls not available."
 #endif
diff --git a/libc/src/poll/linux/ppoll.cpp b/libc/src/poll/linux/ppoll.cpp
index 713e4d497fcb7e..24b6df47a5735f 100644
--- a/libc/src/poll/linux/ppoll.cpp
+++ b/libc/src/poll/linux/ppoll.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/poll/ppoll.h"
+#include "hdr/types/struct_timespec.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/ppoll.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
@@ -23,7 +24,13 @@ LLVM_LIBC_FUNCTION(int, ppoll,
                    (struct pollfd * fds, nfds_t nfds,
                     const struct timespec *__restrict tmo_p,
                     const sigset_t *__restrict sigmask)) {
-  auto result = linux_syscalls::ppoll(fds, nfds, tmo_p, sigmask);
+  timespec ts;
+  timespec *tsp = nullptr;
+  if (tmo_p != nullptr) {
+    ts = *tmo_p;
+    tsp = &ts;
+  }
+  auto result = linux_syscalls::ppoll(fds, nfds, tsp, sigmask);
   if (!result) {
     libc_errno = result.error();
     return -1;
diff --git a/libc/test/src/poll/CMakeLists.txt b/libc/test/src/poll/CMakeLists.txt
index 5097f05bbddb90..0ae920c94d9efd 100644
--- a/libc/test/src/poll/CMakeLists.txt
+++ b/libc/test/src/poll/CMakeLists.txt
@@ -22,12 +22,22 @@ add_libc_test(
   DEPENDS
     libc.hdr.limits_macros
     libc.hdr.poll_macros
+    libc.hdr.signal_macros
+    libc.hdr.sys_time_macros
     libc.hdr.types.sigset_t
+    libc.hdr.types.struct_itimerval
     libc.hdr.types.struct_pollfd
+    libc.hdr.types.struct_sigaction
     libc.hdr.types.struct_timespec
     libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.poll.ppoll
+    libc.src.signal.raise
+    libc.src.signal.sigaction
+    libc.src.signal.sigaddset
+    libc.src.signal.sigemptyset
+    libc.src.signal.sigprocmask
+    libc.src.sys.time.setitimer
     libc.src.unistd.close
     libc.src.unistd.pipe
     libc.src.unistd.read
diff --git a/libc/test/src/poll/ppoll_test.cpp b/libc/test/src/poll/ppoll_test.cpp
index c6ad70e9209879..a6362e559600ef 100644
--- a/libc/test/src/poll/ppoll_test.cpp
+++ b/libc/test/src/poll/ppoll_test.cpp
@@ -13,11 +13,21 @@
 
 #include "hdr/limits_macros.h"
 #include "hdr/poll_macros.h"
+#include "hdr/signal_macros.h"
+#include "hdr/sys_time_macros.h"
 #include "hdr/types/sigset_t.h"
+#include "hdr/types/struct_itimerval.h"
 #include "hdr/types/struct_pollfd.h"
+#include "hdr/types/struct_sigaction.h"
 #include "hdr/types/struct_timespec.h"
 #include "src/__support/CPP/scope.h"
 #include "src/poll/ppoll.h"
+#include "src/signal/raise.h"
+#include "src/signal/sigaction.h"
+#include "src/signal/sigaddset.h"
+#include "src/signal/sigemptyset.h"
+#include "src/signal/sigprocmask.h"
+#include "src/sys/time/setitimer.h"
 #include "src/unistd/close.h"
 #include "src/unistd/pipe.h"
 #include "src/unistd/read.h"
@@ -27,6 +37,12 @@
 
 using LlvmLibcPPollTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 
+static bool sigalrm_handler_called = false;
+extern "C" void handle_sigalrm(int) { sigalrm_handler_called = true; }
+
+static bool sigusr1_handler_called = false;
+extern "C" void handle_sigusr1(int) { sigusr1_handler_called = true; }
+
 TEST_F(LlvmLibcPPollTest, SmokeTest) {
   timespec ts{0, 0};
   int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, nullptr);
@@ -41,21 +57,71 @@ TEST_F(LlvmLibcPPollTest, SmokeFailureTest) {
 }
 
 TEST_F(LlvmLibcPPollTest, TimeoutNotMutated) {
-  const timespec orig_ts{0, 0};
+  sigalrm_handler_called = false;
+  struct sigaction sa {};
+  sa.sa_handler = handle_sigalrm;
+  LIBC_NAMESPACE::sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  struct sigaction old_sa {};
+  ASSERT_EQ(LIBC_NAMESPACE::sigaction(SIGALRM, &sa, &old_sa), 0);
+
+  LIBC_NAMESPACE::cpp::scope_exit restore_sa([&] {
+    LIBC_NAMESPACE::sigaction(SIGALRM, &old_sa, nullptr);
+    struct itimerval disable_timer {};
+    LIBC_NAMESPACE::setitimer(ITIMER_REAL, &disable_timer, nullptr);
+  });
+
+  struct itimerval timer {};
+  timer.it_value.tv_sec = 0;
+  timer.it_value.tv_usec = 100000; // 100ms
+  ASSERT_EQ(LIBC_NAMESPACE::setitimer(ITIMER_REAL, &timer, nullptr), 0);
+
+  const timespec orig_ts{1, 0}; // 1 second
   timespec ts = orig_ts;
   int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, nullptr);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_EQ(0, ret);
+  ASSERT_EQ(-1, ret);
+  ASSERT_ERRNO_EQ(EINTR);
+  ASSERT_TRUE(sigalrm_handler_called);
+
+  // The Linux raw syscall modifies its timeout argument when interrupted by a
+  // signal, but POSIX requires that ppoll does not modify it. Verify that the
+  // timeout argument was not modified.
   ASSERT_EQ(ts.tv_sec, orig_ts.tv_sec);
   ASSERT_EQ(ts.tv_nsec, orig_ts.tv_nsec);
 }
 
 TEST_F(LlvmLibcPPollTest, WithSigmask) {
-  timespec ts{0, 0};
-  sigset_t mask{};
-  int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, &mask);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_EQ(0, ret);
+  sigusr1_handler_called = false;
+  struct sigaction sa {};
+  sa.sa_handler = handle_sigusr1;
+  LIBC_NAMESPACE::sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  struct sigaction old_sa {};
+  ASSERT_EQ(LIBC_NAMESPACE::sigaction(SIGUSR1, &sa, &old_sa), 0);
+
+  sigset_t block_mask{};
+  LIBC_NAMESPACE::sigemptyset(&block_mask);
+  LIBC_NAMESPACE::sigaddset(&block_mask, SIGUSR1);
+  sigset_t orig_mask{};
+  ASSERT_EQ(LIBC_NAMESPACE::sigprocmask(SIG_BLOCK, &block_mask, &orig_mask), 0);
+
+  LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+    LIBC_NAMESPACE::sigprocmask(SIG_SETMASK, &orig_mask, nullptr);
+    LIBC_NAMESPACE::sigaction(SIGUSR1, &old_sa, nullptr);
+  });
+
+  // Raise SIGUSR1 while it is blocked.
+  ASSERT_EQ(LIBC_NAMESPACE::raise(SIGUSR1), 0);
+  ASSERT_FALSE(sigusr1_handler_called);
+
+  // Call ppoll with a mask that unblocks SIGUSR1.
+  sigset_t unblock_mask{};
+  LIBC_NAMESPACE::sigemptyset(&unblock_mask);
+  timespec ts{1, 0};
+  int ret = LIBC_NAMESPACE::ppoll(nullptr, 0, &ts, &unblock_mask);
+  ASSERT_EQ(-1, ret);
+  ASSERT_ERRNO_EQ(EINTR);
+  ASSERT_TRUE(sigusr1_handler_called);
 }
 
 TEST_F(LlvmLibcPPollTest, PipeReadiness) {



More information about the libc-commits mailing list