From e948aa59278ff3f72e68ccaae8ab07eca4a212c3 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 17 Jul 2026 19:18:09 -0300 Subject: [PATCH 1/7] Very hacky initial attempt at converting FPM to internal poll You may need to hack up the poll backends to not return an error on EINTR. Passes a lot of tests, but need to deal with leak strategy. --- sapi/fpm/fpm/fpm_events.c | 133 +++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 1ccf2c9c5490..4c495e7416cb 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -8,6 +8,7 @@ #include #include +#include #include "fpm.h" #include "fpm_process_ctl.h" @@ -40,7 +41,7 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev); static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue); -static struct fpm_event_module_s *module; +static struct php_poll_ctx *module; static struct fpm_event_queue_s *fpm_event_queue_timer = NULL; static struct fpm_event_queue_s *fpm_event_queue_fd = NULL; static struct fpm_event_s children_bury_timer; @@ -185,8 +186,15 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even *queue = elt; /* ask the event module to add the fd from its own queue */ - if (*queue == fpm_event_queue_fd && module->add) { - module->add(ev); + if (*queue == fpm_event_queue_fd) { + uint32_t events = 0; + if (ev->flags & FPM_EV_EDGE) { + events |= PHP_POLL_ET; + } + if (ev->flags & FPM_EV_READ) { + events |= PHP_POLL_READ; + } + ev->index = php_poll_add(module, ev->fd, events, ev) == SUCCESS ? ev->fd : -1; } return 0; @@ -216,8 +224,9 @@ static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_even } /* ask the event module to remove the fd from its own queue */ - if (*queue == fpm_event_queue_fd && module->remove) { - module->remove(ev); + if (*queue == fpm_event_queue_fd) { + php_poll_remove(module, ev->fd); + ev->index = ev->fd; } free(q); @@ -237,8 +246,10 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */ return; } - if (*queue == fpm_event_queue_fd && module->clean) { - module->clean(); + if (*queue == fpm_event_queue_fd && module) { + // XXX: There is no way to clean up without freeing the module. + php_poll_destroy(module); + module = NULL; } q = *queue; @@ -254,44 +265,16 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */ int fpm_event_pre_init(char *mechanism) /* {{{ */ { - /* kqueue */ - module = fpm_event_kqueue_module(); - if (module) { - if (!mechanism || strcasecmp(module->name, mechanism) == 0) { - return 0; - } - } - - /* port */ - module = fpm_event_port_module(); - if (module) { - if (!mechanism || strcasecmp(module->name, mechanism) == 0) { - return 0; - } - } + php_poll_register_backends(); - /* epoll */ - module = fpm_event_epoll_module(); - if (module) { - if (!mechanism || strcasecmp(module->name, mechanism) == 0) { - return 0; - } - } - - /* poll */ - module = fpm_event_poll_module(); - if (module) { - if (!mechanism || strcasecmp(module->name, mechanism) == 0) { - return 0; - } + uint32_t flags = PHP_POLL_FLAG_PERSISTENT; + if (mechanism) { + module = php_poll_create_by_name(mechanism, flags); + } else { + module = php_poll_create(PHP_POLL_BACKEND_AUTO, flags); } - - /* select */ - module = fpm_event_select_module(); if (module) { - if (!mechanism || strcasecmp(module->name, mechanism) == 0) { - return 0; - } + return 0; } if (mechanism) { @@ -305,12 +288,12 @@ int fpm_event_pre_init(char *mechanism) /* {{{ */ const char *fpm_event_mechanism_name(void) { - return module ? module->name : NULL; + return module ? php_poll_backend_name(module) : NULL; } int fpm_event_support_edge_trigger(void) { - return module ? module->support_edge_trigger : 0; + return module ? php_poll_backend_supports_edge_triggering(php_poll_get_backend_type(module)) : 0; } int fpm_event_init_main(void) @@ -323,10 +306,7 @@ int fpm_event_init_main(void) return -1; } - if (!module->wait) { - zlog(ZLOG_ERROR, "Incomplete event implementation. Please open a bug report on https://github.com/php/php-src/issues."); - return -1; - } + const char *module_name = fpm_event_mechanism_name(); /* count the max number of necessary fds for polling */ max = 1; /* only one FD is necessary at startup for the master process signal pipe */ @@ -337,12 +317,16 @@ int fpm_event_init_main(void) } } - if (module->init(max) < 0) { - zlog(ZLOG_ERROR, "Unable to initialize the event module %s", module->name); + if (php_poll_init(module) != SUCCESS) { + php_poll_error poll_error = php_poll_get_error(module); + zlog(ZLOG_ERROR, "Unable to initialize the event module %s: %s", + module_name, + php_poll_error_string(poll_error)); return -1; } + php_poll_set_max_events_hint(module, max); - zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module->name, max); + zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module_name, max); if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) { return -1; @@ -350,6 +334,21 @@ int fpm_event_init_main(void) return 0; } +static int fpm_queue_count(struct fpm_event_queue_s **queue) +{ + int count = 0; + if (!queue) { + return count; + } + struct fpm_event_queue_s *q; + q = *queue; + while (q) { + q = q->next; + count++; + } + return count; +} + void fpm_event_loop(int err) /* {{{ */ { static struct fpm_event_s signal_fd_event; @@ -383,10 +382,11 @@ void fpm_event_loop(int err) /* {{{ */ struct timeval ms; struct timeval tmp; struct timeval now; - unsigned long int timeout; + struct timespec timeout; int ret; /* sanity check */ + /* is a child, nothing to do here */ if (fpm_globals.parent_pid != getpid()) { return; } @@ -409,22 +409,35 @@ void fpm_event_loop(int err) /* {{{ */ /* 1s timeout if none has been set */ if (!timerisset(&ms) || timercmp(&ms, &now, <) || timercmp(&ms, &now, ==)) { - timeout = 1000; + timeout.tv_sec = 0; + timeout.tv_nsec = 1000; } else { timersub(&ms, &now, &tmp); - timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1; + TIMEVAL_TO_TIMESPEC(&tmp, &timeout); + // timeout.tv_nsec += 1000; + // timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1; } - ret = module->wait(fpm_event_queue_fd, timeout); - - /* is a child, nothing to do here */ - if (ret == -2) { - return; - } + /* translate from our linked list to its array */ + /* TODO: change representation to native, don't alloc every iter */ + int max_events = fpm_queue_count(&fpm_event_queue_fd) + 1; + php_poll_event *events = safe_emalloc(max_events, sizeof(*events), 0); + ret = php_poll_wait(module, events, max_events, &timeout); if (ret > 0) { zlog(ZLOG_DEBUG, "event module triggered %d events", ret); + } else if (ret < 0) { + php_poll_error poll_error = php_poll_get_error(module); + zlog(ZLOG_ERROR, "poll error: %s", php_poll_error_string(poll_error)); + } + /* do the logic the modules used to */ + for (int i = 0; i < ret; i++) { + struct fpm_event_s *ev = (struct fpm_event_s *)events[i].data; + if (events[i].data) { + fpm_event_fire(ev); + } } + efree(events); /* trigger timers */ q = fpm_event_queue_timer; From 61485bb32d62b77a01c412365e973278297a6bc6 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 17 Jul 2026 20:57:43 -0300 Subject: [PATCH 2/7] There is no need to free if we're using persistent --- sapi/fpm/fpm/fpm_events.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 4c495e7416cb..dafe35655476 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -246,10 +246,8 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */ return; } - if (*queue == fpm_event_queue_fd && module) { + if (*queue == fpm_event_queue_fd) { // XXX: There is no way to clean up without freeing the module. - php_poll_destroy(module); - module = NULL; } q = *queue; From be8c92e16d14825f1eb6dc9b81cfb7dc609f86c3 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 17 Jul 2026 20:41:32 -0300 Subject: [PATCH 3/7] fpm: remove its own implementation from build --- sapi/fpm/config.m4 | 5 - sapi/fpm/fpm/events/epoll.c | 211 --------------------------- sapi/fpm/fpm/events/epoll.h | 23 --- sapi/fpm/fpm/events/kqueue.c | 200 -------------------------- sapi/fpm/fpm/events/kqueue.h | 23 --- sapi/fpm/fpm/events/poll.c | 267 ----------------------------------- sapi/fpm/fpm/events/poll.h | 23 --- sapi/fpm/fpm/events/port.c | 197 -------------------------- sapi/fpm/fpm/events/port.h | 23 --- sapi/fpm/fpm/events/select.c | 168 ---------------------- sapi/fpm/fpm/events/select.h | 23 --- sapi/fpm/fpm/fpm_events.c | 6 - 12 files changed, 1169 deletions(-) delete mode 100644 sapi/fpm/fpm/events/epoll.c delete mode 100644 sapi/fpm/fpm/events/epoll.h delete mode 100644 sapi/fpm/fpm/events/kqueue.c delete mode 100644 sapi/fpm/fpm/events/kqueue.h delete mode 100644 sapi/fpm/fpm/events/poll.c delete mode 100644 sapi/fpm/fpm/events/poll.h delete mode 100644 sapi/fpm/fpm/events/port.c delete mode 100644 sapi/fpm/fpm/events/port.h delete mode 100644 sapi/fpm/fpm/events/select.c delete mode 100644 sapi/fpm/fpm/events/select.h diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 89c53a0c4d28..d21d1e6370cb 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -464,11 +464,6 @@ if test "$PHP_FPM" != "no"; then fpm/fpm_unix.c \ fpm/fpm_worker_pool.c \ fpm/zlog.c \ - fpm/events/select.c \ - fpm/events/poll.c \ - fpm/events/epoll.c \ - fpm/events/kqueue.c \ - fpm/events/port.c \ " PHP_SELECT_SAPI([fpm], diff --git a/sapi/fpm/fpm/events/epoll.c b/sapi/fpm/fpm/events/epoll.c deleted file mode 100644 index 2de3b463ae59..000000000000 --- a/sapi/fpm/fpm/events/epoll.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#include "../fpm_config.h" -#include "../fpm_events.h" -#include "../fpm.h" -#include "../zlog.h" - -#ifdef HAVE_EPOLL - -#include -#include - -static int fpm_event_epoll_init(int max); -static int fpm_event_epoll_clean(void); -static int fpm_event_epoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); -static int fpm_event_epoll_add(struct fpm_event_s *ev); -static int fpm_event_epoll_remove(struct fpm_event_s *ev); - -static struct fpm_event_module_s epoll_module = { - .name = "epoll", - .support_edge_trigger = 1, - .init = fpm_event_epoll_init, - .clean = fpm_event_epoll_clean, - .wait = fpm_event_epoll_wait, - .add = fpm_event_epoll_add, - .remove = fpm_event_epoll_remove, -}; - -static struct epoll_event *epollfds = NULL; -static int nepollfds = 0; -static int epollfd = -1; - -#endif /* HAVE_EPOLL */ - -struct fpm_event_module_s *fpm_event_epoll_module(void) -{ -#ifdef HAVE_EPOLL - return &epoll_module; -#else - return NULL; -#endif /* HAVE_EPOLL */ -} - -#ifdef HAVE_EPOLL - -/* - * Init the module - */ -static int fpm_event_epoll_init(int max) /* {{{ */ -{ - if (max < 1) { - return 0; - } - - /* init epoll */ - epollfd = epoll_create(max + 1); - if (epollfd < 0) { - zlog(ZLOG_ERROR, "epoll: unable to initialize"); - return -1; - } - - /* allocate fds */ - epollfds = malloc(sizeof(struct epoll_event) * max); - if (!epollfds) { - zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max); - return -1; - } - memset(epollfds, 0, sizeof(struct epoll_event) * max); - - /* save max */ - nepollfds = max; - - return 0; -} -/* }}} */ - -/* - * Clean the module - */ -static int fpm_event_epoll_clean(void) -{ - /* free epollfds */ - if (epollfds) { - free(epollfds); - epollfds = NULL; - } - if (epollfd != -1) { - close(epollfd); - epollfd = -1; - } - - nepollfds = 0; - - return 0; -} - -/* - * wait for events or timeout - */ -static int fpm_event_epoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */ -{ - int ret, i; - - /* ensure we have a clean epoolfds before calling epoll_wait() */ - memset(epollfds, 0, sizeof(struct epoll_event) * nepollfds); - - /* wait for incoming event or timeout */ - ret = epoll_wait(epollfd, epollfds, nepollfds, timeout); - if (ret == -1) { - - /* trigger error unless signal interrupt */ - if (errno != EINTR) { - zlog(ZLOG_WARNING, "epoll_wait() returns %d", errno); - return -1; - } - } - - /* events have been triggered, let's fire them */ - for (i = 0; i < ret; i++) { - - /* do we have a valid ev ptr ? */ - if (!epollfds[i].data.ptr) { - continue; - } - - /* fire the event */ - fpm_event_fire((struct fpm_event_s *)epollfds[i].data.ptr); - - /* sanity check */ - if (fpm_globals.parent_pid != getpid()) { - return -2; - } - } - - return ret; -} -/* }}} */ - -/* - * Add a FD to the fd set - */ -static int fpm_event_epoll_add(struct fpm_event_s *ev) /* {{{ */ -{ - struct epoll_event e; - - /* fill epoll struct */ -#if SIZEOF_SIZE_T == 4 - /* Completely initialize event data to prevent valgrind reports */ - e.data.u64 = 0; -#endif - e.events = EPOLLIN; - e.data.fd = ev->fd; - e.data.ptr = (void *)ev; - - if (ev->flags & FPM_EV_EDGE) { - e.events = e.events | EPOLLET; - } - - /* add the event to epoll internal queue */ - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev->fd, &e) == -1) { - zlog(ZLOG_ERROR, "epoll: unable to add fd %d", ev->fd); - return -1; - } - - /* mark the event as registered */ - ev->index = ev->fd; - return 0; -} -/* }}} */ - -/* - * Remove a FD from the fd set - */ -static int fpm_event_epoll_remove(struct fpm_event_s *ev) /* {{{ */ -{ - struct epoll_event e; - - /* fill epoll struct the same way we did in fpm_event_epoll_add() */ - e.events = EPOLLIN; - e.data.fd = ev->fd; - e.data.ptr = (void *)ev; - - if (ev->flags & FPM_EV_EDGE) { - e.events = e.events | EPOLLET; - } - - /* remove the event from epoll internal queue */ - if (epoll_ctl(epollfd, EPOLL_CTL_DEL, ev->fd, &e) == -1) { - zlog(ZLOG_ERROR, "epoll: unable to remove fd %d", ev->fd); - return -1; - } - - /* mark the event as not registered */ - ev->index = -1; - return 0; -} -/* }}} */ - -#endif /* HAVE_EPOLL */ diff --git a/sapi/fpm/fpm/events/epoll.h b/sapi/fpm/fpm/events/epoll.h deleted file mode 100644 index 8b3d1867c0b1..000000000000 --- a/sapi/fpm/fpm/events/epoll.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#ifndef FPM_EVENTS_EPOLL_H -#define FPM_EVENTS_EPOLL_H - -#include "../fpm_config.h" -#include "../fpm_events.h" - -struct fpm_event_module_s *fpm_event_epoll_module(void); - -#endif /* FPM_EVENTS_EPOLL_H */ diff --git a/sapi/fpm/fpm/events/kqueue.c b/sapi/fpm/fpm/events/kqueue.c deleted file mode 100644 index 22b9d1a8d3ab..000000000000 --- a/sapi/fpm/fpm/events/kqueue.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#include "../fpm_config.h" -#include "../fpm_events.h" -#include "../fpm.h" -#include "../zlog.h" - -#ifdef HAVE_KQUEUE - -#include -#include -#include - -#include - -static int fpm_event_kqueue_init(int max); -static int fpm_event_kqueue_clean(void); -static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); -static int fpm_event_kqueue_add(struct fpm_event_s *ev); -static int fpm_event_kqueue_remove(struct fpm_event_s *ev); - -static struct fpm_event_module_s kqueue_module = { - .name = "kqueue", - .support_edge_trigger = 1, - .init = fpm_event_kqueue_init, - .clean = fpm_event_kqueue_clean, - .wait = fpm_event_kqueue_wait, - .add = fpm_event_kqueue_add, - .remove = fpm_event_kqueue_remove, -}; - -static struct kevent *kevents = NULL; -static int nkevents = 0; -static int kfd = 0; - -#endif /* HAVE_KQUEUE */ - -/* - * Return the module configuration - */ -struct fpm_event_module_s *fpm_event_kqueue_module(void) /* {{{ */ -{ -#ifdef HAVE_KQUEUE - return &kqueue_module; -#else - return NULL; -#endif /* HAVE_KQUEUE */ -} -/* }}} */ - -#ifdef HAVE_KQUEUE - -/* - * init kqueue and stuff - */ -static int fpm_event_kqueue_init(int max) /* {{{ */ -{ - if (max < 1) { - return 0; - } - - kfd = kqueue(); - if (kfd < 0) { - zlog(ZLOG_ERROR, "kqueue: unable to initialize"); - return -1; - } - - kevents = calloc(max, sizeof(struct kevent)); - if (!kevents) { - zlog(ZLOG_ERROR, "kevent: unable to allocate %d events", max); - return -1; - } - - nkevents = max; - - return 0; -} -/* }}} */ - -/* - * release kqueue stuff - */ -static int fpm_event_kqueue_clean(void) /* {{{ */ -{ - if (kevents) { - free(kevents); - kevents = NULL; - } - - nkevents = 0; - - return 0; -} -/* }}} */ - -/* - * wait for events or timeout - */ -static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */ -{ - struct timespec t; - int ret, i; - - /* ensure we have a clean kevents before calling kevent() */ - memset(kevents, 0, sizeof(struct kevent) * nkevents); - - /* convert ms to timespec struct */ - t.tv_sec = timeout / 1000; - t.tv_nsec = (timeout % 1000) * 1000 * 1000; - - /* wait for incoming event or timeout */ - ret = kevent(kfd, NULL, 0, kevents, nkevents, &t); - if (ret == -1) { - - /* trigger error unless signal interrupt */ - if (errno != EINTR) { - zlog(ZLOG_WARNING, "kevent() returns %d", errno); - return -1; - } - } - - /* fire triggered events */ - for (i = 0; i < ret; i++) { - if (kevents[i].udata) { - struct fpm_event_s *ev = (struct fpm_event_s *)kevents[i].udata; - fpm_event_fire(ev); - /* sanity check */ - if (fpm_globals.parent_pid != getpid()) { - return -2; - } - } - } - - return ret; -} -/* }}} */ - -/* - * Add a FD to kevent queue - */ -static int fpm_event_kqueue_add(struct fpm_event_s *ev) /* {{{ */ -{ - struct kevent k; - int flags = EV_ADD; - - if (ev->flags & FPM_EV_EDGE) { - flags = flags | EV_CLEAR; - } - - EV_SET(&k, ev->fd, EVFILT_READ, flags, 0, 0, (void *)ev); - - if (kevent(kfd, &k, 1, NULL, 0, NULL) < 0) { - zlog(ZLOG_ERROR, "kevent: unable to add event"); - return -1; - } - - /* mark the event as registered */ - ev->index = ev->fd; - return 0; -} -/* }}} */ - -/* - * Remove a FD from the kevent queue - */ -static int fpm_event_kqueue_remove(struct fpm_event_s *ev) /* {{{ */ -{ - struct kevent k; - int flags = EV_DELETE; - - if (ev->flags & FPM_EV_EDGE) { - flags = flags | EV_CLEAR; - } - - EV_SET(&k, ev->fd, EVFILT_READ, flags, 0, 0, (void *)ev); - - if (kevent(kfd, &k, 1, NULL, 0, NULL) < 0) { - zlog(ZLOG_ERROR, "kevent: unable to delete event"); - return -1; - } - - /* mark the event as not registered */ - ev->index = -1; - return 0; -} -/* }}} */ - -#endif /* HAVE_KQUEUE */ diff --git a/sapi/fpm/fpm/events/kqueue.h b/sapi/fpm/fpm/events/kqueue.h deleted file mode 100644 index 4690b065dfce..000000000000 --- a/sapi/fpm/fpm/events/kqueue.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#ifndef FPM_EVENTS_KQUEUE_H -#define FPM_EVENTS_KQUEUE_H - -#include "../fpm_config.h" -#include "../fpm_events.h" - -struct fpm_event_module_s *fpm_event_kqueue_module(void); - -#endif /* FPM_EVENTS_KQUEUE_H */ diff --git a/sapi/fpm/fpm/events/poll.c b/sapi/fpm/fpm/events/poll.c deleted file mode 100644 index 10085e83faa8..000000000000 --- a/sapi/fpm/fpm/events/poll.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#include "../fpm_config.h" -#include "../fpm_events.h" -#include "../fpm.h" -#include "../zlog.h" - -#ifdef HAVE_POLL - -#include -#include -#include - -static int fpm_event_poll_init(int max); -static int fpm_event_poll_clean(void); -static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); -static int fpm_event_poll_add(struct fpm_event_s *ev); -static int fpm_event_poll_remove(struct fpm_event_s *ev); - -static struct fpm_event_module_s poll_module = { - .name = "poll", - .support_edge_trigger = 0, - .init = fpm_event_poll_init, - .clean = fpm_event_poll_clean, - .wait = fpm_event_poll_wait, - .add = fpm_event_poll_add, - .remove = fpm_event_poll_remove, -}; - -static struct pollfd *pollfds = NULL; -static struct pollfd *active_pollfds = NULL; -static int npollfds = 0; -static int next_free_slot = 0; -#endif /* HAVE_POLL */ - -/* - * return the module configuration - */ -struct fpm_event_module_s *fpm_event_poll_module(void) -{ -#ifdef HAVE_POLL - return &poll_module; -#else - return NULL; -#endif /* HAVE_POLL */ -} - -#ifdef HAVE_POLL - -/* - * Init the module - */ -static int fpm_event_poll_init(int max) -{ - int i; - - if (max < 1) { - return 0; - } - - /* alloc and clear pollfds */ - pollfds = malloc(sizeof(struct pollfd) * max); - if (!pollfds) { - zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max); - return -1; - } - memset(pollfds, 0, sizeof(struct pollfd) * max); - - /* set all fd to -1 in order to ensure it's not set */ - for (i = 0; i < max; i++) { - pollfds[i].fd = -1; - } - - /* alloc and clear active_pollfds */ - active_pollfds = malloc(sizeof(struct pollfd) * max); - if (!active_pollfds) { - free(pollfds); - zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max); - return -1; - } - memset(active_pollfds, 0, sizeof(struct pollfd) * max); - - /* save max */ - npollfds = max; - return 0; -} - -/* - * Clean the module - */ -static int fpm_event_poll_clean(void) -{ - /* free pollfds */ - if (pollfds) { - free(pollfds); - pollfds = NULL; - } - - /* free active_pollfds */ - if (active_pollfds) { - free(active_pollfds); - active_pollfds = NULL; - } - - npollfds = 0; - return 0; -} - -/* - * wait for events or timeout - */ -static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */ -{ - int ret; - struct fpm_event_queue_s *q; - - if (npollfds > 0) { - /* copy pollfds because poll() alters it */ - memcpy(active_pollfds, pollfds, sizeof(struct pollfd) * npollfds); - } - - /* wait for incoming event or timeout */ - ret = poll(active_pollfds, npollfds, timeout); - if (ret == -1) { - - /* trigger error unless signal interrupt */ - if (errno != EINTR) { - zlog(ZLOG_WARNING, "poll() returns %d", errno); - return -1; - } - } - - /* events have been triggered */ - if (ret > 0) { - - /* trigger POLLIN events */ - q = queue; - while (q) { - /* ensure ev->index is valid */ - if (q->ev && q->ev->index >= 0 && q->ev->index < npollfds && q->ev->fd == active_pollfds[q->ev->index].fd) { - - /* has the event has been triggered ? */ - if (active_pollfds[q->ev->index].revents & POLLIN) { - - /* fire the event */ - fpm_event_fire(q->ev); - - /* sanity check */ - if (fpm_globals.parent_pid != getpid()) { - return -2; - } - } - } - q = q->next; /* iterate */ - } - } - - return ret; -} -/* }}} */ - -/* - * Add a FD to the fd set - */ -static int fpm_event_poll_add(struct fpm_event_s *ev) /* {{{ */ -{ - int i; - - /* do we have a direct free slot */ - if (pollfds[next_free_slot].fd == -1) { - /* register the event */ - pollfds[next_free_slot].fd = ev->fd; - pollfds[next_free_slot].events = POLLIN; - - /* remember the event place in the fd list and suppose next slot is free */ - ev->index = next_free_slot++; - if (next_free_slot >= npollfds) { - next_free_slot = 0; - } - return 0; - } - - /* let's search */ - for (i = 0; i < npollfds; i++) { - if (pollfds[i].fd != -1) { - /* not free */ - continue; - } - - /* register the event */ - pollfds[i].fd = ev->fd; - pollfds[i].events = POLLIN; - - /* remember the event place in the fd list and suppose next slot is free */ - ev->index = next_free_slot++; - if (next_free_slot >= npollfds) { - next_free_slot = 0; - } - return 0; - } - - zlog(ZLOG_ERROR, "poll: not enough space to add event (fd=%d)", ev->fd); - return -1; -} -/* }}} */ - -/* - * Remove a FD from the fd set - */ -static int fpm_event_poll_remove(struct fpm_event_s *ev) /* {{{ */ -{ - int i; - - /* do we have a direct access */ - if (ev->index >= 0 && ev->index < npollfds && pollfds[ev->index].fd == ev->fd) { - /* remember this slot as free */ - next_free_slot = ev->index; - - /* clear event in pollfds */ - pollfds[ev->index].fd = -1; - pollfds[ev->index].events = 0; - - /* mark the event as not registered */ - ev->index = -1; - - return 0; - } - - /* let's search */ - for (i = 0; i < npollfds; i++) { - - if (pollfds[i].fd != ev->fd) { - /* not found */ - continue; - } - - /* remember this slot as free */ - next_free_slot = i; - - /* clear event in pollfds */ - pollfds[i].fd = -1; - pollfds[i].events = 0; - - /* mark the event as not registered */ - ev->index = -1; - - return 0; - } - - zlog(ZLOG_ERROR, "poll: unable to remove event: not found (fd=%d, index=%d)", ev->fd, ev->index); - return -1; -} -/* }}} */ - -#endif /* HAVE_POLL */ diff --git a/sapi/fpm/fpm/events/poll.h b/sapi/fpm/fpm/events/poll.h deleted file mode 100644 index 24fa512eda9e..000000000000 --- a/sapi/fpm/fpm/events/poll.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#ifndef FPM_EVENTS_POLL_H -#define FPM_EVENTS_POLL_H - -#include "../fpm_config.h" -#include "../fpm_events.h" - -struct fpm_event_module_s *fpm_event_poll_module(void); - -#endif /* FPM_EVENTS_POLL_H */ diff --git a/sapi/fpm/fpm/events/port.c b/sapi/fpm/fpm/events/port.c deleted file mode 100644 index b7f5715abad4..000000000000 --- a/sapi/fpm/fpm/events/port.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#include "../fpm_config.h" -#include "../fpm_events.h" -#include "../fpm.h" -#include "../zlog.h" - -#ifdef HAVE_EVENT_PORTS - -#include -#include -#include - -static int fpm_event_port_init(int max); -static int fpm_event_port_clean(void); -static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); -static int fpm_event_port_add(struct fpm_event_s *ev); -static int fpm_event_port_remove(struct fpm_event_s *ev); - -static struct fpm_event_module_s port_module = { - .name = "port", - .support_edge_trigger = 0, - .init = fpm_event_port_init, - .clean = fpm_event_port_clean, - .wait = fpm_event_port_wait, - .add = fpm_event_port_add, - .remove = fpm_event_port_remove, -}; - -port_event_t *events = NULL; -int nevents = 0; -static int pfd = -1; - -#endif /* HAVE_EVENT_PORTS */ - -struct fpm_event_module_s *fpm_event_port_module(void) /* {{{ */ -{ -#ifdef HAVE_EVENT_PORTS - return &port_module; -#else - return NULL; -#endif /* HAVE_EVENT_PORTS */ -} -/* }}} */ - -#ifdef HAVE_EVENT_PORTS - -/* - * Init the module - */ -static int fpm_event_port_init(int max) /* {{{ */ -{ - /* open port */ - pfd = port_create(); - if (pfd < 0) { - zlog(ZLOG_ERROR, "port: unable to initialize port_create()"); - return -1; - } - - if (max < 1) { - return 0; - } - - /* alloc and clear active_pollfds */ - events = malloc(sizeof(port_event_t) * max); - if (!events) { - zlog(ZLOG_ERROR, "port: Unable to allocate %d events", max); - return -1; - } - - nevents = max; - return 0; -} -/* }}} */ - -/* - * Clean the module - */ -static int fpm_event_port_clean(void) -{ - if (pfd > -1) { - close(pfd); - pfd = -1; - } - - if (events) { - free(events); - events = NULL; - } - - nevents = 0; - return 0; -} - -/* - * wait for events or timeout - */ -static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */ -{ - int ret; - unsigned int i, nget; - timespec_t t; - - /* convert timeout into timespec_t */ - t.tv_sec = (int)(timeout / 1000); - t.tv_nsec = (timeout % 1000) * 1000 * 1000; - - /* wait for incoming event or timeout. We want at least one event or timeout */ - nget = 1; - events[0].portev_user = (void *)-1; /* so we can double check that an event was returned */ - - ret = port_getn(pfd, events, nevents, &nget, &t); - if (ret < 0) { - - /* trigger error unless signal interrupt or timeout */ - if (errno != EINTR && errno != ETIME) { - zlog(ZLOG_WARNING, "poll() returns %d", errno); - return -1; - } else if (nget > 0 && events[0].portev_user == (void *)-1) { - /* This confusing API can return an event at the same time - * that it reports EINTR or ETIME. If that occurs, just - * report the event. With EINTR, nget can be > 0 without - * any event, so check that portev_user was filled in. - * - * See discussion thread - * http://marc.info/?l=opensolaris-networking-discuss&m=125071205204540 - */ - nget = 0; - } - } - - for (i = 0; i < nget; i++) { - struct fpm_event_s *ev; - - /* do we have a ptr to the event ? */ - if (!events[i].portev_user) { - continue; - } - - ev = (struct fpm_event_s *)events[i].portev_user; - - /* re-associate for next event */ - fpm_event_port_add(ev); - - /* fire the event */ - fpm_event_fire(ev); - - /* sanity check */ - if (fpm_globals.parent_pid != getpid()) { - return -2; - } - } - return nget; -} -/* }}} */ - -/* - * Add a FD to the fd set - */ -static int fpm_event_port_add(struct fpm_event_s *ev) /* {{{ */ -{ - /* add the event to port */ - if (port_associate(pfd, PORT_SOURCE_FD, ev->fd, POLLIN, (void *)ev) < 0) { - zlog(ZLOG_ERROR, "port: unable to add the event"); - return -1; - } - return 0; -} -/* }}} */ - -/* - * Remove a FD from the fd set - */ -static int fpm_event_port_remove(struct fpm_event_s *ev) /* {{{ */ -{ - /* remove the event from port */ - if (port_dissociate(pfd, PORT_SOURCE_FD, ev->fd) < 0) { - zlog(ZLOG_ERROR, "port: unable to add the event"); - return -1; - } - return 0; -} -/* }}} */ - -#endif /* HAVE_EVENT_PORTS */ diff --git a/sapi/fpm/fpm/events/port.h b/sapi/fpm/fpm/events/port.h deleted file mode 100644 index 5768c891bdac..000000000000 --- a/sapi/fpm/fpm/events/port.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#ifndef FPM_EVENTS_PORT_H -#define FPM_EVENTS_PORT_H - -#include "../fpm_config.h" -#include "../fpm_events.h" - -struct fpm_event_module_s *fpm_event_port_module(void); - -#endif /* FPM_EVENTS_PORT_H */ diff --git a/sapi/fpm/fpm/events/select.c b/sapi/fpm/fpm/events/select.c deleted file mode 100644 index f6132b025747..000000000000 --- a/sapi/fpm/fpm/events/select.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#include "../fpm_config.h" -#include "../fpm_events.h" -#include "../fpm.h" -#include "../zlog.h" - -#ifdef HAVE_SELECT - -/* According to POSIX.1-2001 */ -#include - -/* According to earlier standards */ -#include -#include -#include - -#include - -static int fpm_event_select_init(int max); -static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); -static int fpm_event_select_add(struct fpm_event_s *ev); -static int fpm_event_select_remove(struct fpm_event_s *ev); - -static struct fpm_event_module_s select_module = { - .name = "select", - .support_edge_trigger = 0, - .init = fpm_event_select_init, - .clean = NULL, - .wait = fpm_event_select_wait, - .add = fpm_event_select_add, - .remove = fpm_event_select_remove, -}; - -static fd_set fds; - -#endif /* HAVE_SELECT */ - -/* - * return the module configuration - */ -struct fpm_event_module_s *fpm_event_select_module(void) -{ -#ifdef HAVE_SELECT - return &select_module; -#else - return NULL; -#endif /* HAVE_SELECT */ -} - -#ifdef HAVE_SELECT - -/* - * Init the module - */ -static int fpm_event_select_init(int max) /* {{{ */ -{ - FD_ZERO(&fds); - return 0; -} -/* }}} */ - - -/* - * wait for events or timeout - */ -static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */ -{ - int ret; - struct fpm_event_queue_s *q; - fd_set current_fds; - struct timeval t; - - /* copy fds because select() alters it */ - current_fds = fds; - - /* fill struct timeval with timeout */ - t.tv_sec = timeout / 1000; - t.tv_usec = (timeout % 1000) * 1000; - - /* wait for incoming event or timeout */ - ret = select(FD_SETSIZE, ¤t_fds, NULL, NULL, &t); - if (ret == -1) { - - /* trigger error unless signal interrupt */ - if (errno != EINTR) { - zlog(ZLOG_WARNING, "poll() returns %d", errno); - return -1; - } - } - - /* events have been triggered */ - if (ret > 0) { - - /* trigger POLLIN events */ - q = queue; - while (q) { - if (q->ev) { /* sanity check */ - - /* check if the event has been triggered */ - if (FD_ISSET(q->ev->fd, ¤t_fds)) { - - /* fire the event */ - fpm_event_fire(q->ev); - - /* sanity check */ - if (fpm_globals.parent_pid != getpid()) { - return -2; - } - } - } - q = q->next; /* iterate */ - } - } - return ret; - -} -/* }}} */ - -/* - * Add a FD to the fd set - */ -static int fpm_event_select_add(struct fpm_event_s *ev) /* {{{ */ -{ - /* check size limitation */ - if (ev->fd >= FD_SETSIZE) { - zlog(ZLOG_ERROR, "select: not enough space in the select fd list (max = %d). Please consider using another event mechanism.", FD_SETSIZE); - return -1; - } - - /* add the FD if not already in */ - if (!FD_ISSET(ev->fd, &fds)) { - FD_SET(ev->fd, &fds); - ev->index = ev->fd; - } - - return 0; -} -/* }}} */ - -/* - * Remove a FD from the fd set - */ -static int fpm_event_select_remove(struct fpm_event_s *ev) /* {{{ */ -{ - /* remove the fd if it's in */ - if (FD_ISSET(ev->fd, &fds)) { - FD_CLR(ev->fd, &fds); - ev->index = -1; - } - - return 0; -} -/* }}} */ - -#endif /* HAVE_SELECT */ diff --git a/sapi/fpm/fpm/events/select.h b/sapi/fpm/fpm/events/select.h deleted file mode 100644 index e4cafde1ed9b..000000000000 --- a/sapi/fpm/fpm/events/select.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Jerome Loyet | - +----------------------------------------------------------------------+ -*/ - -#ifndef FPM_EVENTS_SELECT_H -#define FPM_EVENTS_SELECT_H - -#include "../fpm_config.h" -#include "../fpm_events.h" - -struct fpm_event_module_s *fpm_event_select_module(void); - -#endif /* FPM_EVENTS_SELECT_H */ diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index dafe35655476..6b4bb861e3fc 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -21,12 +21,6 @@ #include "fpm_clock.h" #include "fpm_log.h" -#include "events/select.h" -#include "events/poll.h" -#include "events/epoll.h" -#include "events/port.h" -#include "events/kqueue.h" - #ifdef HAVE_SYSTEMD #include "fpm_systemd.h" #endif From ae476ace86dc1d5566ef83220db4a955de05fd36 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 17 Jul 2026 21:42:02 -0300 Subject: [PATCH 4/7] fpm: avoid reallocating every loop iter Allocate the buffer at the beginning. There is a bit of glue for going between the linked list that FPM uses for record keeping vs. the buffer of FDs that poll API returns, but it's minimal. This does leak memory, so a few more tests fail. Need to figure out strategy for that. --- sapi/fpm/fpm/fpm_events.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 6b4bb861e3fc..8998bcf12366 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -39,6 +39,8 @@ static struct php_poll_ctx *module; static struct fpm_event_queue_s *fpm_event_queue_timer = NULL; static struct fpm_event_queue_s *fpm_event_queue_fd = NULL; static struct fpm_event_s children_bury_timer; +static php_poll_event *events; +static int max_events; static void fpm_event_cleanup(int which, void *arg) /* {{{ */ { @@ -291,7 +293,6 @@ int fpm_event_support_edge_trigger(void) int fpm_event_init_main(void) { struct fpm_worker_pool_s *wp; - int max; if (!module) { zlog(ZLOG_ERROR, "no event module found"); @@ -301,11 +302,11 @@ int fpm_event_init_main(void) const char *module_name = fpm_event_mechanism_name(); /* count the max number of necessary fds for polling */ - max = 1; /* only one FD is necessary at startup for the master process signal pipe */ + max_events = 1; /* only one FD is necessary at startup for the master process signal pipe */ for (wp = fpm_worker_all_pools; wp; wp = wp->next) { if (!wp->config) continue; if (wp->config->catch_workers_output && wp->config->pm_max_children > 0) { - max += (wp->config->pm_max_children * 2); + max_events += (wp->config->pm_max_children * 2); } } @@ -316,9 +317,10 @@ int fpm_event_init_main(void) php_poll_error_string(poll_error)); return -1; } - php_poll_set_max_events_hint(module, max); + php_poll_set_max_events_hint(module, max_events); + events = safe_emalloc(max_events, sizeof(*events), true); - zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module_name, max); + zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module_name, max_events); if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) { return -1; @@ -326,21 +328,6 @@ int fpm_event_init_main(void) return 0; } -static int fpm_queue_count(struct fpm_event_queue_s **queue) -{ - int count = 0; - if (!queue) { - return count; - } - struct fpm_event_queue_s *q; - q = *queue; - while (q) { - q = q->next; - count++; - } - return count; -} - void fpm_event_loop(int err) /* {{{ */ { static struct fpm_event_s signal_fd_event; @@ -411,9 +398,6 @@ void fpm_event_loop(int err) /* {{{ */ } /* translate from our linked list to its array */ - /* TODO: change representation to native, don't alloc every iter */ - int max_events = fpm_queue_count(&fpm_event_queue_fd) + 1; - php_poll_event *events = safe_emalloc(max_events, sizeof(*events), 0); ret = php_poll_wait(module, events, max_events, &timeout); if (ret > 0) { @@ -429,7 +413,6 @@ void fpm_event_loop(int err) /* {{{ */ fpm_event_fire(ev); } } - efree(events); /* trigger timers */ q = fpm_event_queue_timer; From 2cd04718aeb6dd5da9cba918f072feff11e3c5da Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Sat, 18 Jul 2026 19:35:14 -0300 Subject: [PATCH 5/7] fpm: ignore EINTR-derived errors In the poll/kqueue/epoll backends, these would return an error without a warning or processing the results. Do the same here. Note that the old fpm Solaris event ports backend could handle the case of EINTR/ETIME with returned results. I don't think the internal polling API handles that, but I don't have a Solaris system to test with. --- sapi/fpm/fpm/fpm_events.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 8998bcf12366..919ee598f891 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -404,7 +404,10 @@ void fpm_event_loop(int err) /* {{{ */ zlog(ZLOG_DEBUG, "event module triggered %d events", ret); } else if (ret < 0) { php_poll_error poll_error = php_poll_get_error(module); - zlog(ZLOG_ERROR, "poll error: %s", php_poll_error_string(poll_error)); + /* we don't care about signal interrupts */ + if (poll_error != PHP_POLL_ERROR_CODE_INTERRUPTED) { + zlog(ZLOG_ERROR, "poll error: %s", php_poll_error_string(poll_error)); + } } /* do the logic the modules used to */ for (int i = 0; i < ret; i++) { From 7de14e8f9d75b7f2aa301d5f545b995feead338a Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Sat, 18 Jul 2026 19:44:26 -0300 Subject: [PATCH 6/7] fpm: fix problematic memory management Don't use ZendMM; relocate comment to more sensible spot. --- sapi/fpm/fpm/fpm_events.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 919ee598f891..2bbbd46c47a4 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -35,17 +35,19 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev); static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue); -static struct php_poll_ctx *module; +static struct php_poll_ctx *module = NULL; static struct fpm_event_queue_s *fpm_event_queue_timer = NULL; static struct fpm_event_queue_s *fpm_event_queue_fd = NULL; static struct fpm_event_s children_bury_timer; -static php_poll_event *events; +static php_poll_event *events = NULL; static int max_events; static void fpm_event_cleanup(int which, void *arg) /* {{{ */ { fpm_event_queue_destroy(&fpm_event_queue_timer); fpm_event_queue_destroy(&fpm_event_queue_fd); + // XXX: Freeing the poll context causes more problems than not. + free(events); } /* }}} */ @@ -242,10 +244,6 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */ return; } - if (*queue == fpm_event_queue_fd) { - // XXX: There is no way to clean up without freeing the module. - } - q = *queue; while (q) { tmp = q; @@ -318,7 +316,7 @@ int fpm_event_init_main(void) return -1; } php_poll_set_max_events_hint(module, max_events); - events = safe_emalloc(max_events, sizeof(*events), true); + events = calloc(max_events, sizeof(*events)); zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module_name, max_events); From 4de00ea0f16c0694b1040e549cf47c9977e6df6b Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Sat, 18 Jul 2026 19:47:27 -0300 Subject: [PATCH 7/7] fpm: rename module to poll_ctx --- sapi/fpm/fpm/fpm_events.c | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 2bbbd46c47a4..624651486534 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -35,7 +35,7 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev); static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue); -static struct php_poll_ctx *module = NULL; +static struct php_poll_ctx *poll_ctx = NULL; static struct fpm_event_queue_s *fpm_event_queue_timer = NULL; static struct fpm_event_queue_s *fpm_event_queue_fd = NULL; static struct fpm_event_s children_bury_timer; @@ -192,7 +192,7 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even if (ev->flags & FPM_EV_READ) { events |= PHP_POLL_READ; } - ev->index = php_poll_add(module, ev->fd, events, ev) == SUCCESS ? ev->fd : -1; + ev->index = php_poll_add(poll_ctx, ev->fd, events, ev) == SUCCESS ? ev->fd : -1; } return 0; @@ -223,7 +223,7 @@ static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_even /* ask the event module to remove the fd from its own queue */ if (*queue == fpm_event_queue_fd) { - php_poll_remove(module, ev->fd); + php_poll_remove(poll_ctx, ev->fd); ev->index = ev->fd; } @@ -261,11 +261,11 @@ int fpm_event_pre_init(char *mechanism) /* {{{ */ uint32_t flags = PHP_POLL_FLAG_PERSISTENT; if (mechanism) { - module = php_poll_create_by_name(mechanism, flags); + poll_ctx = php_poll_create_by_name(mechanism, flags); } else { - module = php_poll_create(PHP_POLL_BACKEND_AUTO, flags); + poll_ctx = php_poll_create(PHP_POLL_BACKEND_AUTO, flags); } - if (module) { + if (poll_ctx) { return 0; } @@ -280,24 +280,24 @@ int fpm_event_pre_init(char *mechanism) /* {{{ */ const char *fpm_event_mechanism_name(void) { - return module ? php_poll_backend_name(module) : NULL; + return poll_ctx ? php_poll_backend_name(poll_ctx) : NULL; } int fpm_event_support_edge_trigger(void) { - return module ? php_poll_backend_supports_edge_triggering(php_poll_get_backend_type(module)) : 0; + return poll_ctx ? php_poll_backend_supports_edge_triggering(php_poll_get_backend_type(poll_ctx)) : 0; } int fpm_event_init_main(void) { struct fpm_worker_pool_s *wp; - if (!module) { - zlog(ZLOG_ERROR, "no event module found"); + if (!poll_ctx) { + zlog(ZLOG_ERROR, "no event poll_ctx found"); return -1; } - const char *module_name = fpm_event_mechanism_name(); + const char *poll_ctx_name = fpm_event_mechanism_name(); /* count the max number of necessary fds for polling */ max_events = 1; /* only one FD is necessary at startup for the master process signal pipe */ @@ -308,17 +308,17 @@ int fpm_event_init_main(void) } } - if (php_poll_init(module) != SUCCESS) { - php_poll_error poll_error = php_poll_get_error(module); - zlog(ZLOG_ERROR, "Unable to initialize the event module %s: %s", - module_name, + if (php_poll_init(poll_ctx) != SUCCESS) { + php_poll_error poll_error = php_poll_get_error(poll_ctx); + zlog(ZLOG_ERROR, "Unable to initialize the event poll_ctx %s: %s", + poll_ctx_name, php_poll_error_string(poll_error)); return -1; } - php_poll_set_max_events_hint(module, max_events); + php_poll_set_max_events_hint(poll_ctx, max_events); events = calloc(max_events, sizeof(*events)); - zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module_name, max_events); + zlog(ZLOG_DEBUG, "event poll_ctx is %s and %d fds have been reserved", poll_ctx_name, max_events); if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) { return -1; @@ -396,18 +396,18 @@ void fpm_event_loop(int err) /* {{{ */ } /* translate from our linked list to its array */ - ret = php_poll_wait(module, events, max_events, &timeout); + ret = php_poll_wait(poll_ctx, events, max_events, &timeout); if (ret > 0) { - zlog(ZLOG_DEBUG, "event module triggered %d events", ret); + zlog(ZLOG_DEBUG, "event poll_ctx triggered %d events", ret); } else if (ret < 0) { - php_poll_error poll_error = php_poll_get_error(module); + php_poll_error poll_error = php_poll_get_error(poll_ctx); /* we don't care about signal interrupts */ if (poll_error != PHP_POLL_ERROR_CODE_INTERRUPTED) { zlog(ZLOG_ERROR, "poll error: %s", php_poll_error_string(poll_error)); } } - /* do the logic the modules used to */ + /* do the logic the event module used to */ for (int i = 0; i < ret; i++) { struct fpm_event_s *ev = (struct fpm_event_s *)events[i].data; if (events[i].data) {