Add epoll_pwait2().
I've also added doc comments for everything in <sys/epoll.h>.
I've also broken up the old "smoke" test (which was taking 2s on my
riscv64 qemu) to keep the total runtime for all the tests down to 200ms.
Test: treehugger
Change-Id: Icd939af51886fdf21432653a07373c1a0f26e422
diff --git a/docs/status.md b/docs/status.md
index de2fa10..4b1b46b 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -63,7 +63,7 @@
multi-threaded C).
* `mbsrtowcs_l` and `wcsrtombs_l` aliases for `mbsrtowcs` and `wcsrtombs`.
* New system call wrappers: `__riscv_flush_icache` (`<sys/cachectl.h>`),
- `__riscv_hwprobe` (`<sys/hwprobe.h>`).
+ `__riscv_hwprobe` (`<sys/hwprobe.h>`), `epoll_pwait2`/`epoll_pwait2_64` (`<sys/epoll.h>`).
New libc behavior in V (API level 35):
* Added `LD_SHOW_AUXV` to the dynamic linker to dump the ELF auxiliary
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index f4f9e1b..5791f19 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -322,6 +322,7 @@
int __epoll_create1:epoll_create1(int) all
int epoll_ctl(int, int op, int, struct epoll_event*) all
int __epoll_pwait:epoll_pwait(int, struct epoll_event*, int, int, const sigset64_t*, size_t) all
+int __epoll_pwait2:epoll_pwait2(int, struct epoll_event*, int, const timespec64*, const sigset64_t*, size_t) all
int __eventfd:eventfd2(unsigned int, int) all
diff --git a/libc/bionic/sys_epoll.cpp b/libc/bionic/sys_epoll.cpp
index cffa173..be97818 100644
--- a/libc/bionic/sys_epoll.cpp
+++ b/libc/bionic/sys_epoll.cpp
@@ -34,6 +34,8 @@
extern "C" int __epoll_create1(int flags);
extern "C" int __epoll_pwait(int, epoll_event*, int, int, const sigset64_t*, size_t);
+extern "C" int __epoll_pwait2(int, epoll_event*, int, const __kernel_timespec*, const sigset64_t*,
+ size_t);
int epoll_create(int size) {
if (size <= 0) {
@@ -56,6 +58,31 @@
return __epoll_pwait(fd, events, max_events, timeout, ss, sizeof(*ss));
}
+int epoll_pwait2(int fd, epoll_event* events, int max_events, const timespec* timeout,
+ const sigset_t* ss) {
+ SigSetConverter set{ss};
+ return epoll_pwait2_64(fd, events, max_events, timeout, set.ptr);
+}
+
+int epoll_pwait2_64(int fd, epoll_event* events, int max_events, const timespec* timeout,
+ const sigset64_t* ss) {
+ // epoll_pwait2() is our first syscall that assumes a 64-bit time_t even for
+ // 32-bit processes, so for ILP32 we need to convert.
+ // TODO: factor this out into a TimeSpecConverter as/when we get more syscalls like this.
+#if __LP64__
+ const __kernel_timespec* kts_ptr = reinterpret_cast<const __kernel_timespec*>(timeout);
+#else
+ __kernel_timespec kts;
+ const __kernel_timespec* kts_ptr = nullptr;
+ if (timeout) {
+ kts.tv_sec = timeout->tv_sec;
+ kts.tv_nsec = timeout->tv_nsec;
+ kts_ptr = &kts;
+ }
+#endif
+ return __epoll_pwait2(fd, events, max_events, kts_ptr, ss, sizeof(*ss));
+}
+
int epoll_wait(int fd, struct epoll_event* events, int max_events, int timeout) {
return epoll_pwait64(fd, events, max_events, timeout, nullptr);
}
diff --git a/libc/include/sys/epoll.h b/libc/include/sys/epoll.h
index 2091b90..2bad655 100644
--- a/libc/include/sys/epoll.h
+++ b/libc/include/sys/epoll.h
@@ -26,8 +26,12 @@
* SUCH DAMAGE.
*/
-#ifndef _SYS_EPOLL_H_
-#define _SYS_EPOLL_H_
+#pragma once
+
+/**
+ * @file sys/epoll.h
+ * @brief I/O event file descriptors.
+ */
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -37,14 +41,67 @@
__BEGIN_DECLS
+/**
+ * [epoll_create(2)](http://man7.org/linux/man-pages/man2/epoll_create.2.html)
+ * creates a new [epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)
+ * file descriptor.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
int epoll_create(int __size);
+
+/**
+ * [epoll_create1(2)](http://man7.org/linux/man-pages/man2/epoll_create1.2.html)
+ * creates a new [epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)
+ * file descriptor with the given flags.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
int epoll_create1(int __flags);
+/**
+ * [epoll_ctl(2)](http://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
+ * adds/modifies/removes file descriptors from the given epoll file descriptor.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int epoll_ctl(int __epoll_fd, int __op, int __fd, struct epoll_event* __BIONIC_COMPLICATED_NULLNESS __event);
+
+/**
+ * [epoll_wait(2)](http://man7.org/linux/man-pages/man2/epoll_wait.2.html)
+ * waits for an event on the given epoll file descriptor.
+ *
+ * Returns the number of ready file descriptors on success, 0 on timeout,
+ * or -1 and sets `errno` on failure.
+ */
int epoll_wait(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, int __timeout_ms);
+
+/**
+ * Like epoll_wait() but atomically applying the given signal mask.
+ */
int epoll_pwait(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, int __timeout_ms, const sigset_t* _Nullable __mask);
+
+/**
+ * Like epoll_pwait() but using a 64-bit signal mask even on 32-bit systems.
+ *
+ * Available since API level 28.
+ */
int epoll_pwait64(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, int __timeout_ms, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(28);
-__END_DECLS
+/**
+ * Like epoll_pwait() but with a `struct timespec` timeout, for nanosecond resolution.
+ *
+ * Available since API level 35.
+ */
+int epoll_pwait2(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, const struct timespec* _Nullable __timeout, const sigset_t* _Nullable __mask) __INTRODUCED_IN(35);
-#endif
+/**
+ * Like epoll_pwait2() but using a 64-bit signal mask even on 32-bit systems.
+ *
+ * Available since API level 35.
+ */
+int epoll_pwait2_64(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, const struct timespec* _Nullable __timeout, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(35);
+
+__END_DECLS
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index e90618d..75b3d86 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1586,6 +1586,8 @@
LIBC_V { # introduced=VanillaIceCream
global:
+ epoll_pwait2;
+ epoll_pwait2_64;
localtime_rz;
mbsrtowcs_l;
mktime_z;
diff --git a/tests/sys_epoll_test.cpp b/tests/sys_epoll_test.cpp
index fb2a48f..8dee93f 100644
--- a/tests/sys_epoll_test.cpp
+++ b/tests/sys_epoll_test.cpp
@@ -24,29 +24,101 @@
#include "utils.h"
-TEST(sys_epoll, smoke) {
+TEST(sys_epoll, epoll_wait) {
int epoll_fd = epoll_create(1);
- ASSERT_NE(-1, epoll_fd) << strerror(errno);
- epoll_event events[1];
+ ASSERT_NE(-1, epoll_fd);
// Regular epoll_wait.
+ epoll_event events[1] = {};
ASSERT_EQ(0, epoll_wait(epoll_fd, events, 1, 1));
+}
+
+TEST(sys_epoll, epoll_pwait_no_sigset) {
+ int epoll_fd = epoll_create(1);
+ ASSERT_NE(-1, epoll_fd);
// epoll_pwait without a sigset (which is equivalent to epoll_wait).
+ epoll_event events[1] = {};
ASSERT_EQ(0, epoll_pwait(epoll_fd, events, 1, 1, nullptr));
+}
+TEST(sys_epoll, epoll_pwait64_no_sigset) {
#if defined(__BIONIC__)
+ int epoll_fd = epoll_create(1);
+ ASSERT_NE(-1, epoll_fd);
+
// epoll_pwait64 without a sigset (which is equivalent to epoll_wait).
+ epoll_event events[1] = {};
ASSERT_EQ(0, epoll_pwait64(epoll_fd, events, 1, 1, nullptr));
+#else
+ GTEST_SKIP() << "epoll_pwait64 is bionic-only";
#endif
+}
+
+TEST(sys_epoll, epoll_pwait2_no_sigset) {
+#if defined(__BIONIC__)
+ int epoll_fd = epoll_create(1);
+ ASSERT_NE(-1, epoll_fd);
+
+ // epoll_pwait2 without a sigset (which is equivalent to epoll_wait).
+ epoll_event events[1] = {};
+ timespec ts = {.tv_nsec = 500};
+ int rc = epoll_pwait2(epoll_fd, events, 1, &ts, nullptr);
+ if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no epoll_pwait2 in this kernel";
+ ASSERT_EQ(0, rc) << strerror(errno);
+#else
+ GTEST_SKIP() << "epoll_pwait2 is only in glibc 2.35+";
+#endif
+}
+
+TEST(sys_epoll, epoll_pwait_with_sigset) {
+ int epoll_fd = epoll_create(1);
+ ASSERT_NE(-1, epoll_fd);
// epoll_pwait with a sigset.
+ epoll_event events[1] = {};
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGPIPE);
ASSERT_EQ(0, epoll_pwait(epoll_fd, events, 1, 1, &ss));
}
+TEST(sys_epoll, epoll_pwait2_with_sigset) {
+ int epoll_fd = epoll_create(1);
+ ASSERT_NE(-1, epoll_fd);
+
+#if defined(__BIONIC__)
+ epoll_event events[1] = {};
+ timespec ts = {.tv_nsec = 500};
+ sigset_t ss2;
+ sigemptyset(&ss2);
+ sigaddset(&ss2, SIGPIPE);
+ int rc = epoll_pwait2(epoll_fd, events, 1, &ts, &ss2);
+ if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no epoll_pwait2 in this kernel";
+ ASSERT_EQ(0, rc) << strerror(errno);
+#else
+ GTEST_SKIP() << "epoll_pwait2 is only in glibc 2.35+";
+#endif
+}
+
+TEST(sys_epoll, epoll_pwait2_64_with_sigset) {
+ int epoll_fd = epoll_create(1);
+ ASSERT_NE(-1, epoll_fd);
+
+#if defined(__BIONIC__)
+ epoll_event events[1] = {};
+ timespec ts = {.tv_nsec = 500};
+ sigset64_t ss2;
+ sigemptyset64(&ss2);
+ sigaddset64(&ss2, SIGPIPE);
+ int rc = epoll_pwait2_64(epoll_fd, events, 1, &ts, &ss2);
+ if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no epoll_pwait2 in this kernel";
+ ASSERT_EQ(0, rc) << strerror(errno);
+#else
+ GTEST_SKIP() << "epoll_pwait2_64 is bionic-only";
+#endif
+}
+
TEST(sys_epoll, epoll_create_invalid_size) {
errno = 0;
ASSERT_EQ(-1, epoll_create(0));