From 4c62e59fe6b1de5a72d818ea3e8712f70b65ef90 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Thu, 13 Jul 2023 15:45:33 -0700 Subject: [PATCH] Add epoll_pwait2(). I've also added doc comments for everything in . 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 --- docs/status.md | 2 +- libc/SYSCALLS.TXT | 1 + libc/bionic/sys_epoll.cpp | 27 ++++++++++++++ libc/include/sys/epoll.h | 65 ++++++++++++++++++++++++++++++-- libc/libc.map.txt | 2 + tests/sys_epoll_test.cpp | 78 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 167 insertions(+), 8 deletions(-) diff --git a/docs/status.md b/docs/status.md index de2fa10cd..4b1b46bb6 100644 --- a/docs/status.md +++ b/docs/status.md @@ -63,7 +63,7 @@ New libc functions in V (API level 35): multi-threaded C). * `mbsrtowcs_l` and `wcsrtombs_l` aliases for `mbsrtowcs` and `wcsrtombs`. * New system call wrappers: `__riscv_flush_icache` (``), - `__riscv_hwprobe` (``). + `__riscv_hwprobe` (``), `epoll_pwait2`/`epoll_pwait2_64` (``). 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 f4f9e1b5f..5791f193b 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -322,6 +322,7 @@ ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int) all 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 cffa17380..be97818da 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 @@ int epoll_pwait64(int fd, epoll_event* events, int max_events, int timeout, cons 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(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 2091b9090..2bad65529 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 #include @@ -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 e90618dfb..75b3d8667 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1586,6 +1586,8 @@ LIBC_U { # introduced=UpsideDownCake 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 fb2a48fd0..8dee93fab 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));