Skip to content

Commit 04d1b82

Browse files
committed
Fall back to epoll_wait when epoll_pwait2 is unavailable at runtime
HAVE_EPOLL_PWAIT2 only tells whether the libc exports the wrapper, which glibc does since 2.35 regardless of the running kernel. A PHP built on a kernel with epoll_pwait2 and run on one older than 5.11 gets ENOSYS from every Context::wait() call, which makes the epoll backend and thus the Auto backend unusable. The same happens under emulation layers that do not implement the syscall. Try epoll_pwait2 first and on ENOSYS or ENOTSUP switch the process to epoll_wait with a millisecond timeout, retrying the current call so the failure is never visible to the caller. The flag is process wide since kernel support is the same for every thread, and it is atomic so the first concurrent waits in a ZTS build do not race on it.
1 parent 04066f5 commit 04d1b82

1 file changed

Lines changed: 17 additions & 4 deletions

File tree

‎main/poll/poll_backend_epoll.c‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818

1919
#include <sys/epoll.h>
2020

21+
#ifdef HAVE_EPOLL_PWAIT2
22+
/* Cleared when the running kernel returns ENOSYS */
23+
static zend_atomic_bool epoll_pwait2_available = ZEND_ATOMIC_BOOL_INITIALIZER(true);
24+
#endif
25+
2126
typedef struct epoll_backend_data {
2227
int epoll_fd;
2328
struct epoll_event *events;
@@ -182,11 +187,19 @@ static int epoll_backend_wait(
182187

183188
int nfds;
184189
#ifdef HAVE_EPOLL_PWAIT2
185-
nfds = epoll_pwait2(backend_data->epoll_fd, backend_data->events, max_events, timeout, NULL);
186-
#else
187-
int timeout_ms = php_poll_timespec_to_ms(timeout);
188-
nfds = epoll_wait(backend_data->epoll_fd, backend_data->events, max_events, timeout_ms);
190+
if (EXPECTED(zend_atomic_bool_load_ex(&epoll_pwait2_available))) {
191+
nfds = epoll_pwait2(
192+
backend_data->epoll_fd, backend_data->events, max_events, timeout, NULL);
193+
if (UNEXPECTED(nfds < 0 && (errno == ENOSYS || errno == ENOTSUP))) {
194+
zend_atomic_bool_store_ex(&epoll_pwait2_available, false);
195+
}
196+
}
197+
if (UNEXPECTED(!zend_atomic_bool_load_ex(&epoll_pwait2_available)))
189198
#endif
199+
{
200+
int timeout_ms = php_poll_timespec_to_ms(timeout);
201+
nfds = epoll_wait(backend_data->epoll_fd, backend_data->events, max_events, timeout_ms);
202+
}
190203

191204
if (nfds > 0) {
192205
for (int i = 0; i < nfds; i++) {

0 commit comments

Comments
 (0)