[libc-commits] [libc] [libc] Implement ppoll in poll (PR #225767)
Aman Maurya via libc-commits
libc-commits at lists.llvm.org
Thu Sep 24 03:17:10 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/5] [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/5] [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/5] [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/5] [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) {
>From 292533a449c9d817b4317d987eb820b60db3fa0b Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Thu, 24 Sep 2026 15:46:10 +0530
Subject: [PATCH 5/5] [libc] Fix formatting in ppoll_test
Assisted by Gemini.
---
libc/test/src/poll/ppoll_test.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libc/test/src/poll/ppoll_test.cpp b/libc/test/src/poll/ppoll_test.cpp
index a6362e559600ef..98a5e76e791170 100644
--- a/libc/test/src/poll/ppoll_test.cpp
+++ b/libc/test/src/poll/ppoll_test.cpp
@@ -58,20 +58,20 @@ TEST_F(LlvmLibcPPollTest, SmokeFailureTest) {
TEST_F(LlvmLibcPPollTest, TimeoutNotMutated) {
sigalrm_handler_called = false;
- struct sigaction sa {};
+ struct sigaction sa{};
sa.sa_handler = handle_sigalrm;
LIBC_NAMESPACE::sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- struct sigaction old_sa {};
+ 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 {};
+ struct itimerval disable_timer{};
LIBC_NAMESPACE::setitimer(ITIMER_REAL, &disable_timer, nullptr);
});
- struct itimerval timer {};
+ 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);
@@ -92,11 +92,11 @@ TEST_F(LlvmLibcPPollTest, TimeoutNotMutated) {
TEST_F(LlvmLibcPPollTest, WithSigmask) {
sigusr1_handler_called = false;
- struct sigaction sa {};
+ struct sigaction sa{};
sa.sa_handler = handle_sigusr1;
LIBC_NAMESPACE::sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- struct sigaction old_sa {};
+ struct sigaction old_sa{};
ASSERT_EQ(LIBC_NAMESPACE::sigaction(SIGUSR1, &sa, &old_sa), 0);
sigset_t block_mask{};
More information about the libc-commits
mailing list