From: Jérôme Loyet Date: Sat, 8 Oct 2011 21:04:10 +0000 (+0000) Subject: - Implemented FR #52569 (Add the "ondemand" process-manager to allow zero children) X-Git-Tag: php-5.3.9RC1~45 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e7c6f9c62df9c921be93c89e4afdb522fb53e0f7;p=php - Implemented FR #52569 (Add the "ondemand" process-manager to allow zero children) --- diff --git a/NEWS b/NEWS index bcb7b27b6a..6439ab99d7 100644 --- a/NEWS +++ b/NEWS @@ -75,6 +75,8 @@ PHP NEWS . Fixed bug #53872 (internal corruption of phar). (Hannes) - PHP-FPM SAPI: + . Implemented FR #52569 (Add the "ondemand" process-manager + to allow zero children). (fat) . Fixed bug #55486 (status show BIG processes number). (fat) . Fixed bug #55577 (status.html does not install). (fat) . Backported from 5.4 branch (Dropped restriction of not setting the same diff --git a/sapi/fpm/Makefile.frag b/sapi/fpm/Makefile.frag index 85a0ba70f9..c5cea7e523 100644 --- a/sapi/fpm/Makefile.frag +++ b/sapi/fpm/Makefile.frag @@ -2,6 +2,7 @@ fpm: $(SAPI_FPM_PATH) $(builddir)/fpm: @mkdir -p $(builddir)/fpm + @mkdir -p $(builddir)/fpm/events $(SAPI_FPM_PATH): $(builddir)/fpm $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(SAPI_EXTRA_DEPS) $(BUILD_FPM) diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 064493c602..953fa1f7bc 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -366,6 +366,171 @@ AC_DEFUN([AC_FPM_TIMES], ]) dnl }}} +AC_DEFUN([AC_FPM_KQUEUE], +[ + AC_MSG_CHECKING([for kqueue]) + + AC_TRY_COMPILE( + [ + #include + #include + #include + ], [ + int kfd; + struct kevent k; + kfd = kqueue(); + /* 0 -> STDIN_FILENO */ + EV_SET(&k, 0, EVFILT_READ , EV_ADD | EV_CLEAR, 0, 0, NULL); + ], [ + AC_DEFINE([HAVE_KQUEUE], 1, [do we have kqueue?]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) +]) +dnl }}} + +AC_DEFUN([AC_FPM_PORT], +[ + AC_MSG_CHECKING([for port framework]) + + AC_TRY_COMPILE( + [ + #include + ], [ + int port; + + port = port_create(); + if (port < 0) { + return 1; + } + ], [ + AC_DEFINE([HAVE_PORT], 1, [do we have port framework?]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) +]) +dnl }}} + +AC_DEFUN([AC_FPM_DEVPOLL], +[ + AC_MSG_CHECKING([for /dev/poll]) + + AC_TRY_COMPILE( + [ + #include + #include + ], [ + int n, dp; + struct dvpoll dvp; + dp = 0; + dvp.dp_fds = NULL; + dvp.dp_nfds = 0; + dvp.dp_timeout = 0; + n = ioctl(dp, DP_POLL, &dvp) + ], [ + AC_DEFINE([HAVE_DEVPOLL], 1, [do we have /dev/poll?]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) +]) +dnl }}} + +AC_DEFUN([AC_FPM_EPOLL], +[ + AC_MSG_CHECKING([for epoll]) + + AC_TRY_COMPILE( + [ + #include + ], [ + int epollfd; + struct epoll_event e; + + epollfd = epoll_create(1); + if (epollfd < 0) { + return 1; + } + + e.events = EPOLLIN | EPOLLET; + e.data.fd = 0; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, 0, &e) == -1) { + return 1; + } + + e.events = 0; + if (epoll_wait(epollfd, &e, 1, 1) < 0) { + return 1; + } + ], [ + AC_DEFINE([HAVE_EPOLL], 1, [do we have epoll?]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) +]) +dnl }}} + +AC_DEFUN([AC_FPM_POLL], +[ + AC_MSG_CHECKING([for poll]) + + AC_TRY_COMPILE( + [ + #include + ], [ + struct pollfd fds[2]; + + fds[0].fd = 0; + fds[0].events = POLLIN; + + fds[1].fd = 0; + fds[1].events = POLLIN; + + poll(fds, 2, 1); + ], [ + AC_DEFINE([HAVE_POLL], 1, [do we have poll?]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) +]) +dnl }}} + +AC_DEFUN([AC_FPM_SELECT], +[ + AC_MSG_CHECKING([for select]) + + AC_TRY_COMPILE( + [ + /* According to POSIX.1-2001 */ + #include + + /* According to earlier standards */ + #include + #include + #include + ], [ + fd_set fds; + struct timeval t; + t.tv_sec = 0; + t.tv_usec = 42; + FD_ZERO(&fds); + /* 0 -> STDIN_FILENO */ + FD_SET(0, &fds); + select(FD_SETSIZE, &fds, NULL, NULL, &t); + ], [ + AC_DEFINE([HAVE_SELECT], 1, [do we have select?]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + ]) +]) +dnl }}} + AC_MSG_CHECKING(for FPM build) if test "$PHP_FPM" != "no"; then @@ -379,6 +544,12 @@ if test "$PHP_FPM" != "no"; then AC_FPM_LQ AC_FPM_SYSCONF AC_FPM_TIMES + AC_FPM_KQUEUE + AC_FPM_PORT + AC_FPM_DEVPOLL + AC_FPM_EPOLL + AC_FPM_POLL + AC_FPM_SELECT PHP_ARG_WITH(fpm-user,, [ --with-fpm-user[=USER] Set the user for php-fpm to run as. (default: nobody)], nobody, no) @@ -447,6 +618,12 @@ 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/devpoll.c \ + fpm/events/port.c \ " PHP_SELECT_SAPI(fpm, program, $PHP_FPM_FILES $PHP_FPM_TRACE_FILES, $PHP_FPM_CFLAGS, '$(SAPI_FPM_PATH)') diff --git a/sapi/fpm/fpm/events/devpoll.c b/sapi/fpm/fpm/events/devpoll.c new file mode 100644 index 0000000000..b3850cc2d4 --- /dev/null +++ b/sapi/fpm/fpm/events/devpoll.c @@ -0,0 +1,248 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "../fpm_config.h" +#include "../fpm_events.h" +#include "../fpm.h" +#include "../zlog.h" + +#if HAVE_DEVPOLL + +#include +#include +#include +#include +#include +#include + +static int fpm_event_devpoll_init(int max); +static int fpm_event_devpoll_clean(); +static int fpm_event_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); +static int fpm_event_devpoll_add(struct fpm_event_s *ev); +static int fpm_event_devpoll_remove(struct fpm_event_s *ev); + +static struct fpm_event_module_s devpoll_module = { + .name = "/dev/poll", + .support_edge_trigger = 0, + .init = fpm_event_devpoll_init, + .clean = fpm_event_devpoll_clean, + .wait = fpm_event_devpoll_wait, + .add = fpm_event_devpoll_add, + .remove = fpm_event_devpoll_remove, +}; + +int dpfd = -1; +static struct pollfd *pollfds = NULL; +static struct pollfd *active_pollfds = NULL; +static int npollfds = 0; + +#endif /* HAVE_DEVPOLL */ + +struct fpm_event_module_s *fpm_event_devpoll_module() /* {{{ */ +{ +#if HAVE_DEVPOLL + return &devpoll_module; +#else + return NULL; +#endif /* HAVE_DEVPOLL */ +} +/* }}} */ + +#if HAVE_DEVPOLL + +/* + * Init module + */ +static int fpm_event_devpoll_init(int max) /* {{{ */ +{ + int i; + + /* open /dev/poll for future usages */ + dpfd = open("/dev/poll", O_RDWR); + if (dpfd < 0) { + zlog(ZLOG_ERROR, "Unable to open /dev/poll"); + return -1; + } + + 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_devpoll_clean() /* {{{ */ +{ + /* close /dev/poll if open */ + if (dpfd > -1) { + close(dpfd); + dpfd = -1; + } + + /* 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_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */ +{ + int ret, i; + struct fpm_event_queue_s *q; + struct dvpoll dopoll; + + /* setup /dev/poll */ + dopoll.dp_fds = active_pollfds; + dopoll.dp_nfds = npollfds; + dopoll.dp_timeout = (int)timeout; + + /* wait for inconming event or timeout */ + ret = ioctl(dpfd, DP_POLL, &dopoll); + + if (ret < 0) { + + /* trigger error unless signal interrupt */ + if (errno != EINTR) { + zlog(ZLOG_WARNING, "/dev/poll: ioctl() returns %d", errno); + return -1; + } + } + + /* iterate throught triggered events */ + for (i = 0; i < ret; i++) { + + /* find the corresponding event */ + q = queue; + while (q) { + + /* found */ + if (q->ev && q->ev->fd == active_pollfds[i].fd) { + + /* fire the event */ + fpm_event_fire(q->ev); + + /* sanity check */ + if (fpm_globals.parent_pid != getpid()) { + return -2; + } + break; /* next triggered event */ + } + q = q->next; /* iterate */ + } + } + + return ret; +} +/* }}} */ + +/* + * Add a FD from the fd set + */ +static int fpm_event_devpoll_add(struct fpm_event_s *ev) /* {{{ */ +{ + struct pollfd pollfd; + + /* fill pollfd with event informations */ + pollfd.fd = ev->fd; + pollfd.events = POLLIN; + pollfd.revents = 0; + + /* add the event to the internal queue */ + if (write(dpfd, &pollfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) { + zlog(ZLOG_ERROR, "/dev/poll: Unable to add the event in the internal queue"); + return -1; + } + + /* mark the event as registered */ + ev->index = ev->fd; + + return 0; +} +/* }}} */ + +/* + * Remove a FD from the fd set + */ +static int fpm_event_devpoll_remove(struct fpm_event_s *ev) /* {{{ */ +{ + struct pollfd pollfd; + + /* fill pollfd with the same informations as fpm_event_devpoll_add */ + pollfd.fd = ev->fd; + pollfd.events = POLLIN | POLLREMOVE; + pollfd.revents = 0; + + /* add the event to the internal queue */ + if (write(dpfd, &pollfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) { + zlog(ZLOG_ERROR, "/dev/poll: Unable to remove the event in the internal queue"); + return -1; + } + + /* mark the event as registered */ + ev->index = -1; + + return 0; +} +/* }}} */ + +#endif /* HAVE_DEVPOLL */ diff --git a/sapi/fpm/fpm/events/devpoll.h b/sapi/fpm/fpm/events/devpoll.h new file mode 100644 index 0000000000..add3b50a1b --- /dev/null +++ b/sapi/fpm/fpm/events/devpoll.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef FPM_EVENTS_DEVPOLL_H +#define FPM_EVENTS_DEVPOLL_H + +#include "../fpm_config.h" +#include "../fpm_events.h" + +struct fpm_event_module_s *fpm_event_devpoll_module(); + +#endif /* FPM_EVENTS_DEVPOLL_H */ diff --git a/sapi/fpm/fpm/events/epoll.c b/sapi/fpm/fpm/events/epoll.c new file mode 100644 index 0000000000..8c959486f6 --- /dev/null +++ b/sapi/fpm/fpm/events/epoll.c @@ -0,0 +1,211 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "../fpm_config.h" +#include "../fpm_events.h" +#include "../fpm.h" +#include "../zlog.h" + +#if HAVE_EPOLL + +#include +#include + +static int fpm_event_epoll_init(int max); +static int fpm_event_epoll_clean(); +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 = 0; + +#endif /* HAVE_EPOLL */ + +struct fpm_event_module_s *fpm_event_epoll_module() /* {{{ */ +{ +#if HAVE_EPOLL + return &epoll_module; +#else + return NULL; +#endif /* HAVE_EPOLL */ +} +/* }}} */ + +#if 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() /* {{{ */ +{ + /* free epollfds */ + if (epollfds) { + free(epollfds); + epollfds = NULL; + } + + 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 inconming 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 */ + 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 new file mode 100644 index 0000000000..15983e2637 --- /dev/null +++ b/sapi/fpm/fpm/events/epoll.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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(); + +#endif /* FPM_EVENTS_EPOLL_H */ diff --git a/sapi/fpm/fpm/events/kqueue.c b/sapi/fpm/fpm/events/kqueue.c new file mode 100644 index 0000000000..e2591c6eb2 --- /dev/null +++ b/sapi/fpm/fpm/events/kqueue.c @@ -0,0 +1,208 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "../fpm_config.h" +#include "../fpm_events.h" +#include "../fpm.h" +#include "../zlog.h" + +#if HAVE_KQUEUE + +#include +#include +#include + +#include + +static int fpm_event_kqueue_init(int max); +static int fpm_event_kqueue_clean(); +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() /* {{{ */ +{ +#if HAVE_KQUEUE + return &kqueue_module; +#else + return NULL; +#endif /* HAVE_KQUEUE */ +} +/* }}} */ + +#if 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 = malloc(sizeof(struct kevent) * max); + if (!kevents) { + zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max); + return -1; + } + + memset(kevents, 0, sizeof(struct kevent) * max); + + nkevents = max; + + return 0; +} +/* }}} */ + +/* + * release kqueue stuff + */ +static int fpm_event_kqueue_clean() /* {{{ */ +{ + 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, "epoll_wait() 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 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 add event"); + return -1; + } + + /* mark the vent 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 new file mode 100644 index 0000000000..885de512ef --- /dev/null +++ b/sapi/fpm/fpm/events/kqueue.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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(); + +#endif /* FPM_EVENTS_KQUEUE_H */ diff --git a/sapi/fpm/fpm/events/poll.c b/sapi/fpm/fpm/events/poll.c new file mode 100644 index 0000000000..f11f0b345c --- /dev/null +++ b/sapi/fpm/fpm/events/poll.c @@ -0,0 +1,276 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "../fpm_config.h" +#include "../fpm_events.h" +#include "../fpm.h" +#include "../zlog.h" + +#if HAVE_POLL + +#include +#include +#include + +static int fpm_event_poll_init(int max); +static int fpm_event_poll_clean(); +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() /* {{{ */ +{ +#if HAVE_POLL + return &poll_module; +#else + return NULL; +#endif /* HAVE_POLL */ +} +/* }}} */ + +#if 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() /* {{{ */ +{ + /* 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 inconming 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 enought 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 new file mode 100644 index 0000000000..8eb525ffd9 --- /dev/null +++ b/sapi/fpm/fpm/events/poll.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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(); + +#endif /* FPM_EVENTS_POLL_H */ diff --git a/sapi/fpm/fpm/events/port.c b/sapi/fpm/fpm/events/port.c new file mode 100644 index 0000000000..2e4e553d8b --- /dev/null +++ b/sapi/fpm/fpm/events/port.c @@ -0,0 +1,185 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "../fpm_config.h" +#include "../fpm_events.h" +#include "../fpm.h" +#include "../zlog.h" + +#if HAVE_PORT + +#include +#include +#include + +static int fpm_event_port_init(int max); +static int fpm_event_port_clean(); +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_PORT */ + +struct fpm_event_module_s *fpm_event_port_module() /* {{{ */ +{ +#if HAVE_PORT + return &port_module; +#else + return NULL; +#endif /* HAVE_PORT */ +} +/* }}} */ + +#if HAVE_PORT + +/* + * 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() /* {{{ */ +{ + 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, 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 inconming event or timeout. We want at least one event or timeout */ + nget = 1; + 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; + } + } + + for (i = 0; i < nget; i++) { + + /* do we have a ptr to the event ? */ + if (!events[i].portev_user) { + continue; + } + + /* fire the event */ + fpm_event_fire((struct fpm_event_s *)events[i].portev_user); + + /* 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_PORT */ diff --git a/sapi/fpm/fpm/events/port.h b/sapi/fpm/fpm/events/port.h new file mode 100644 index 0000000000..4f3dc0639b --- /dev/null +++ b/sapi/fpm/fpm/events/port.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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(); + +#endif /* FPM_EVENTS_PORT_H */ diff --git a/sapi/fpm/fpm/events/select.c b/sapi/fpm/fpm/events/select.c new file mode 100644 index 0000000000..7a5091239d --- /dev/null +++ b/sapi/fpm/fpm/events/select.c @@ -0,0 +1,175 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "../fpm_config.h" +#include "../fpm_events.h" +#include "../fpm.h" +#include "../zlog.h" + +#if 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() /* {{{ */ +{ +#if HAVE_SELECT + return &select_module; +#else + return NULL; +#endif /* HAVE_SELECT */ +} +/* }}} */ + +#if 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 inconming 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 new file mode 100644 index 0000000000..c1e6838a9c --- /dev/null +++ b/sapi/fpm/fpm/events/select.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2011 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jerome Loyet | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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(); + +#endif /* FPM_EVENTS_SELECT_H */ diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c index 383a75ddc8..35058b0ea1 100644 --- a/sapi/fpm/fpm/fpm_children.c +++ b/sapi/fpm/fpm/fpm_children.c @@ -371,6 +371,12 @@ int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to } else { max = wp->running_children + nb_to_spawn; } + } else if (wp->config->pm == PM_STYLE_ONDEMAND) { + if (!in_event_loop) { /* starting */ + max = 0; /* do not create any child at startup */ + } else { + max = wp->running_children + nb_to_spawn; + } } else { /* PM_STYLE_STATIC */ max = wp->config->pm_max_children; } @@ -428,6 +434,22 @@ int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */ { + if (wp->config->pm == PM_STYLE_ONDEMAND) { + wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s)); + + if (!wp->ondemand_event) { + zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name); + // FIXME handle crash + return 1; + } + + memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s)); + fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp); + wp->socket_event_set = 1; + fpm_event_add(wp->ondemand_event, 0); + + return 1; + } return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1); } /* }}} */ diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index bcfe618b60..82a45e5da6 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -43,6 +43,7 @@ #include "fpm_shm.h" #include "fpm_status.h" #include "fpm_log.h" +#include "fpm_events.h" #include "zlog.h" #define STR2STR(a) (a ? a : "undefined") @@ -52,6 +53,7 @@ static int fpm_conf_load_ini_file(char *filename TSRMLS_DC); static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset); +static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset); static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset); @@ -93,6 +95,7 @@ static struct ini_value_parser_s ini_fpm_global_options[] = { { "daemonize", &fpm_conf_set_boolean, GO(daemonize) }, { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) }, { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) }, + { "events.mechanism", &fpm_conf_set_string, GO(events_mechanism) }, { 0, 0, 0 } }; @@ -114,6 +117,7 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = { { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) }, { "pm.min_spare_servers", &fpm_conf_set_integer, WPO(pm_min_spare_servers) }, { "pm.max_spare_servers", &fpm_conf_set_integer, WPO(pm_max_spare_servers) }, + { "pm.process_idle_timeout", &fpm_conf_set_time, WPO(pm_process_idle_timeout) }, { "pm.max_requests", &fpm_conf_set_integer, WPO(pm_max_requests) }, { "pm.status_path", &fpm_conf_set_string, WPO(pm_status_path) }, { "ping.path", &fpm_conf_set_string, WPO(ping_path) }, @@ -235,6 +239,22 @@ static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset) / } /* }}} */ +static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset) /* {{{ */ +{ + char *val = Z_STRVAL_P(value); + char *p; + + for (p = val; *p; p++) { + if ( p == val && *p == '-' ) continue; + if (*p < '0' || *p > '9') { + return "is not a valid number (greater or equal than zero)"; + } + } + * (long int *) ((char *) *config + offset) = atol(val); + return NULL; +} +/* }}} */ + static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {{{ */ { char *val = Z_STRVAL_P(value); @@ -487,8 +507,10 @@ static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset) /* {{{ c->pm = PM_STYLE_STATIC; } else if (!strcasecmp(val, "dynamic")) { c->pm = PM_STYLE_DYNAMIC; + } else if (!strcasecmp(val, "ondemand")) { + c->pm = PM_STYLE_ONDEMAND; } else { - return "invalid process manager (static or dynamic)"; + return "invalid process manager (static, dynamic or ondemand)"; } return NULL; } @@ -554,6 +576,7 @@ static void *fpm_worker_pool_config_alloc() /* {{{ */ memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s)); wp->config->listen_backlog = FPM_BACKLOG_DEFAULT; + wp->config->pm_process_idle_timeout = 10; /* 10s by default */ if (!fpm_worker_all_pools) { fpm_worker_all_pools = wp; @@ -719,8 +742,8 @@ static int fpm_conf_process_all_pools() /* {{{ */ } /* pm */ - if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC) { - zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static or dynamic)", wp->config->name); + if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC && wp->config->pm != PM_STYLE_ONDEMAND) { + zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static, dynamic or ondemand)", wp->config->name); return -1; } @@ -763,7 +786,28 @@ static int fpm_conf_process_all_pools() /* {{{ */ zlog(ZLOG_ALERT, "[pool %s] pm.start_servers(%d) must not be less than pm.min_spare_servers(%d) and not greater than pm.max_spare_servers(%d)", wp->config->name, config->pm_start_servers, config->pm_min_spare_servers, config->pm_max_spare_servers); return -1; } + } else if (wp->config->pm == PM_STYLE_ONDEMAND) { + struct fpm_worker_pool_config_s *config = wp->config; + + if (!fpm_event_support_edge_trigger()) { + zlog(ZLOG_ALERT, "[pool %s] ondemand process manager can ONLY be used when events.mechanisme is either epoll (Linux) or kqueue (*BSD).", wp->config->name); + return -1; + } + + if (config->pm_process_idle_timeout < 1) { + zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout); + return -1; + } + + if (config->listen_backlog < FPM_BACKLOG_DEFAULT) { + zlog(ZLOG_WARNING, "[pool %s] listen.backlog(%d) was too low for the ondemand process manager. I updated it for you to %d.", wp->config->name, config->listen_backlog, FPM_BACKLOG_DEFAULT); + config->listen_backlog = FPM_BACKLOG_DEFAULT; + } + /* certainely useless but proper */ + config->pm_start_servers = 0; + config->pm_min_spare_servers = 0; + config->pm_max_spare_servers = 0; } /* status */ @@ -1075,6 +1119,10 @@ static int fpm_conf_post_process(TSRMLS_D) /* {{{ */ return -1; } + if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) { + return -1; + } + if (0 > fpm_conf_process_all_pools()) { return -1; } @@ -1097,6 +1145,7 @@ static void fpm_conf_cleanup(int which, void *arg) /* {{{ */ { free(fpm_global_config.pid_file); free(fpm_global_config.error_log); + free(fpm_global_config.events_mechanism); fpm_global_config.pid_file = 0; fpm_global_config.error_log = 0; #ifdef HAVE_SYSLOG_H @@ -1436,6 +1485,7 @@ static void fpm_conf_dump() /* {{{ */ zlog(ZLOG_NOTICE, "\tdaemonize = %s", BOOL2STR(fpm_global_config.daemonize)); zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files); zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core); + zlog(ZLOG_NOTICE, "\tevents.mechanism = %s", fpm_event_machanism_name()); zlog(ZLOG_NOTICE, " "); for (wp = fpm_worker_all_pools; wp; wp = wp->next) { @@ -1456,6 +1506,7 @@ static void fpm_conf_dump() /* {{{ */ zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers); zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d", wp->config->pm_min_spare_servers); zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d", wp->config->pm_max_spare_servers); + zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d", wp->config->pm_process_idle_timeout); zlog(ZLOG_NOTICE, "\tpm.max_requests = %d", wp->config->pm_max_requests); zlog(ZLOG_NOTICE, "\tpm.status_path = %s", STR2STR(wp->config->pm_status_path)); zlog(ZLOG_NOTICE, "\tping.path = %s", STR2STR(wp->config->ping_path)); diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h index 56556e95a4..9fbd5064c0 100644 --- a/sapi/fpm/fpm/fpm_conf.h +++ b/sapi/fpm/fpm/fpm_conf.h @@ -8,7 +8,7 @@ #include #include "php.h" -#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : "dynamic") +#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : (a == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand")) #define FPM_CONF_MAX_PONG_LENGTH 64 @@ -38,6 +38,7 @@ struct fpm_global_config_s { int daemonize; int rlimit_files; int rlimit_core; + char *events_mechanism; }; extern struct fpm_global_config_s fpm_global_config; @@ -61,6 +62,7 @@ struct fpm_worker_pool_config_s { int pm_start_servers; int pm_min_spare_servers; int pm_max_spare_servers; + int pm_process_idle_timeout; int pm_max_requests; char *pm_status_path; char *ping_path; @@ -89,7 +91,8 @@ struct ini_value_parser_s { enum { PM_STYLE_STATIC = 1, - PM_STYLE_DYNAMIC = 2 + PM_STYLE_DYNAMIC = 2, + PM_STYLE_ONDEMAND = 3 }; int fpm_conf_init_main(int test_conf); diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 1e95e1eef2..ab3a69df86 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -10,7 +10,6 @@ #include #include -#include #include "fpm.h" #include "fpm_process_ctl.h" @@ -23,13 +22,14 @@ #include "fpm_clock.h" #include "fpm_log.h" -#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout); +#include "events/select.h" +#include "events/poll.h" +#include "events/epoll.h" +#include "events/devpoll.h" +#include "events/port.h" +#include "events/kqueue.h" -typedef struct fpm_event_queue_s { - struct fpm_event_queue_s *prev; - struct fpm_event_queue_s *next; - struct fpm_event_s *ev; -} fpm_event_queue; +#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout); static void fpm_event_cleanup(int which, void *arg); static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg); @@ -38,16 +38,12 @@ 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 int fpm_event_nfds_max; +static struct fpm_event_module_s *module; static struct fpm_event_queue_s *fpm_event_queue_timer = NULL; static struct fpm_event_queue_s *fpm_event_queue_fd = NULL; -static php_pollfd *fpm_event_ufds = NULL; static void fpm_event_cleanup(int which, void *arg) /* {{{ */ { - if (fpm_event_ufds) { - free(fpm_event_ufds); - } fpm_event_queue_destroy(&fpm_event_queue_timer); fpm_event_queue_destroy(&fpm_event_queue_fd); } @@ -166,6 +162,11 @@ 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); + } + return 0; } /* }}} */ @@ -189,6 +190,12 @@ static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_even *queue = q->next; (*queue)->prev = NULL; } + + /* ask the event module to remove the fd from its own queue */ + if (*queue == fpm_event_queue_fd && module->remove) { + module->remove(ev); + } + free(q); return 0; } @@ -205,6 +212,11 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */ if (!queue) { return; } + + if (*queue == fpm_event_queue_fd && module->clean) { + module->clean(); + } + q = *queue; while (q) { tmp = q; @@ -216,27 +228,107 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */ } /* }}} */ +int fpm_event_pre_init(char *machanism) /* {{{ */ +{ + /* kqueue */ + module = fpm_event_kqueue_module(); + if (module) { + if (!machanism || strcasecmp(module->name, machanism) == 0) { + return 0; + } + } + + /* port */ + module = fpm_event_port_module(); + if (module) { + if (!machanism || strcasecmp(module->name, machanism) == 0) { + return 0; + } + } + + /* epoll */ + module = fpm_event_epoll_module(); + if (module) { + if (!machanism || strcasecmp(module->name, machanism) == 0) { + return 0; + } + } + + /* /dev/poll */ + module = fpm_event_devpoll_module(); + if (module) { + if (!machanism || strcasecmp(module->name, machanism) == 0) { + return 0; + } + } + + /* poll */ + module = fpm_event_poll_module(); + if (module) { + if (!machanism || strcasecmp(module->name, machanism) == 0) { + return 0; + } + } + + /* select */ + module = fpm_event_select_module(); + if (module) { + if (!machanism || strcasecmp(module->name, machanism) == 0) { + return 0; + } + } + + if (machanism) { + zlog(ZLOG_ERROR, "event mechanism '%s' is not available on this system", machanism); + } else { + zlog(ZLOG_ERROR, "unable to find a suitable event mechanism on this system"); + } + return -1; +} +/* }} */ + +const char *fpm_event_machanism_name() /* {{{ */ +{ + return module ? module->name : NULL; +} +/* }}} */ + +int fpm_event_support_edge_trigger() /* {{{ */ +{ + return module ? module->support_edge_trigger : 0; +} +/* }}} */ + int fpm_event_init_main() /* {{{ */ { struct fpm_worker_pool_s *wp; + int max; + + if (!module) { + zlog(ZLOG_ERROR, "no event module found"); + return -1; + } + + if (!module->wait) { + zlog(ZLOG_ERROR, "Incomplete event implementation. Please open a bug report on https://bugs.php.net."); + return -1; + } /* count the max number of necessary fds for polling */ - fpm_event_nfds_max = 1; /* only one FD is necessary at startup for the master process signal pipe */ + max = 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) { - fpm_event_nfds_max += (wp->config->pm_max_children * 2); + max += (wp->config->pm_max_children * 2); } } - /* malloc the max number of necessary fds for polling */ - fpm_event_ufds = malloc(sizeof(php_pollfd) * fpm_event_nfds_max); - if (!fpm_event_ufds) { - zlog(ZLOG_SYSERROR, "Error while initializing events: malloc() failed"); + if (module->init(max) < 0) { + zlog(ZLOG_ERROR, "Unable to initialize the event module %s", module->name); return -1; } - zlog(ZLOG_DEBUG, "%d fds have been reserved", fpm_event_nfds_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; @@ -273,7 +365,7 @@ void fpm_event_loop(int err) /* {{{ */ struct timeval tmp; struct timeval now; unsigned long int timeout; - int i, ret; + int ret; /* sanity check */ if (fpm_globals.parent_pid != getpid()) { @@ -304,41 +396,15 @@ void fpm_event_loop(int err) /* {{{ */ timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1; } - /* init fpm_event_ufds for php_poll2 */ - memset(fpm_event_ufds, 0, sizeof(php_pollfd) * fpm_event_nfds_max); - i = 0; - q = fpm_event_queue_fd; - while (q && i < fpm_event_nfds_max) { - fpm_event_ufds[i].fd = q->ev->fd; - fpm_event_ufds[i].events = POLLIN; - q->ev->index = i++; - q = q->next; + ret = module->wait(fpm_event_queue_fd, timeout); + + /* is a child, nothing to do here */ + if (ret == -2) { + return; } - /* wait for inconming event or timeout */ - if ((ret = php_poll2(fpm_event_ufds, i, timeout)) == -1) { - if (errno != EINTR) { - zlog(ZLOG_SYSERROR, "failed to wait for events: php_poll2()"); - } - } else if (ret > 0) { - - /* trigger POLLIN events */ - q = fpm_event_queue_fd; - while (q) { - if (q->ev && q->ev->index >= 0 && q->ev->index < fpm_event_nfds_max) { - if (q->ev->fd == fpm_event_ufds[q->ev->index].fd) { - if (fpm_event_ufds[q->ev->index].revents & POLLIN) { - fpm_event_fire(q->ev); - /* sanity check */ - if (fpm_globals.parent_pid != getpid()) { - return; - } - } - } - q->ev->index = -1; - } - q = q->next; - } + if (ret > 0) { + zlog(ZLOG_DEBUG, "event module triggered %d events", ret); } /* trigger timers */ @@ -446,11 +512,11 @@ int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency) /* {{{ */ int fpm_event_del(struct fpm_event_s *ev) /* {{{ */ { - if (fpm_event_queue_del(&fpm_event_queue_fd, ev) != 0) { + if (ev->index >= 0 && fpm_event_queue_del(&fpm_event_queue_fd, ev) != 0) { return -1; } - if (fpm_event_queue_del(&fpm_event_queue_timer, ev) != 0) { + if (ev->index < 0 && fpm_event_queue_del(&fpm_event_queue_timer, ev) != 0) { return -1; } diff --git a/sapi/fpm/fpm/fpm_events.h b/sapi/fpm/fpm/fpm_events.h index 586b242835..416ec6e728 100644 --- a/sapi/fpm/fpm/fpm_events.h +++ b/sapi/fpm/fpm/fpm_events.h @@ -8,6 +8,7 @@ #define FPM_EV_TIMEOUT (1 << 0) #define FPM_EV_READ (1 << 1) #define FPM_EV_PERSIST (1 << 2) +#define FPM_EV_EDGE (1 << 3) #define fpm_event_set_timer(ev, flags, cb, arg) fpm_event_set((ev), -1, (flags), (cb), (arg)) @@ -22,11 +23,30 @@ struct fpm_event_s { short which; /* type of event */ }; +typedef struct fpm_event_queue_s { + struct fpm_event_queue_s *prev; + struct fpm_event_queue_s *next; + struct fpm_event_s *ev; +} fpm_event_queue; + +struct fpm_event_module_s { + const char *name; + int support_edge_trigger; + int (*init)(int max_fd); + int (*clean)(void); + int (*wait)(struct fpm_event_queue_s *queue, unsigned long int timeout); + int (*add)(struct fpm_event_s *ev); + int (*remove)(struct fpm_event_s *ev); +}; + void fpm_event_loop(int err); void fpm_event_fire(struct fpm_event_s *ev); int fpm_event_init_main(); int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg); int fpm_event_add(struct fpm_event_s *ev, unsigned long int timeout); int fpm_event_del(struct fpm_event_s *ev); +int fpm_event_pre_init(char *machanism); +const char *fpm_event_machanism_name(); +int fpm_event_support_edge_trigger(); #endif diff --git a/sapi/fpm/fpm/fpm_process_ctl.c b/sapi/fpm/fpm/fpm_process_ctl.c index 027b566bb2..5d80aedbdb 100644 --- a/sapi/fpm/fpm/fpm_process_ctl.c +++ b/sapi/fpm/fpm/fpm_process_ctl.c @@ -355,6 +355,23 @@ static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ } fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard); + /* this is specific to PM_STYLE_ONDEMAND */ + if (wp->config->pm == PM_STYLE_ONDEMAND) { + struct timeval last, now; + + zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle); + + if (!last_idle_child) continue; + + fpm_request_last_activity(last_idle_child, &last); + fpm_clock_get(&now); + if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) { + last_idle_child->idle_kill = 1; + fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT); + } + + continue; + } /* the rest is only used by PM_STYLE_DYNAMIC */ if (wp->config->pm != PM_STYLE_DYNAMIC) continue; @@ -472,3 +489,47 @@ void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, } /* }}} */ +void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ +{ + struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg; + struct fpm_child_s *child; + + + if (fpm_globals.parent_pid != getpid()) { + /* prevent a event race condition when child process + * have not set up its own event loop */ + return; + } + + wp->socket_event_set = 0; + +// zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children); + + if (wp->running_children >= wp->config->pm_max_children) { + if (!wp->warn_max_children) { + fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard); + zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children); + wp->warn_max_children = 1; + } + + return; + } + + for (child = wp->children; child; child = child->next) { + /* if there is at least on idle child, it will handle the connection, stop here */ + if (fpm_request_is_idle(child)) { + return; + } + } + + wp->warn_max_children = 0; + fpm_children_make(wp, 1, 1, 1); + + if (fpm_globals.is_child) { + return; + } + + zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name); +} +/* }}} */ + diff --git a/sapi/fpm/fpm/fpm_process_ctl.h b/sapi/fpm/fpm/fpm_process_ctl.h index 6bfcfd8c6b..ad755e612a 100644 --- a/sapi/fpm/fpm/fpm_process_ctl.h +++ b/sapi/fpm/fpm/fpm_process_ctl.h @@ -22,6 +22,7 @@ int fpm_pctl_kill(pid_t pid, int how); void fpm_pctl_kill_all(int signo); void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg); void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg); +void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg); int fpm_pctl_child_exited(); int fpm_pctl_init_main(); diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c index f7cd815f64..7b9e0f7679 100644 --- a/sapi/fpm/fpm/fpm_request.c +++ b/sapi/fpm/fpm/fpm_request.c @@ -295,3 +295,20 @@ int fpm_request_is_idle(struct fpm_child_s *child) /* {{{ */ return proc->request_stage == FPM_REQUEST_ACCEPTING; } /* }}} */ + +int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv) /* {{{ */ +{ + struct fpm_scoreboard_proc_s *proc; + + if (!tv) return -1; + + proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i); + if (!proc) { + return -1; + } + + *tv = proc->tv; + + return 1; +} +/* }}} */ diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h index 05cca228b1..aebd36cff4 100644 --- a/sapi/fpm/fpm/fpm_request.h +++ b/sapi/fpm/fpm/fpm_request.h @@ -18,6 +18,7 @@ struct timeval; void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *tv, int terminate_timeout, int slowlog_timeout); int fpm_request_is_idle(struct fpm_child_s *child); const char *fpm_request_get_stage_name(int stage); +int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv); enum fpm_request_stage_e { FPM_REQUEST_ACCEPTING = 1, diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h index 100c3689cb..6688e6d3ba 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.h +++ b/sapi/fpm/fpm/fpm_worker_pool.h @@ -38,6 +38,10 @@ struct fpm_worker_pool_s { struct fpm_scoreboard_s *scoreboard; int log_fd; char **limit_extensions; + + /* for ondemand PM */ + struct fpm_event_s *ondemand_event; + int socket_event_set; }; struct fpm_worker_pool_s *fpm_worker_pool_alloc(); diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index 92738d308c..4e7952bdd2 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -89,6 +89,16 @@ ; Default Value: system defined value ;rlimit_core = 0 +; Specify the event mechanism FPM will use. The following is available: +; - select (any POSIX os) +; - poll (any POSIX os) +; - epoll (linux >= 2.5.44) +; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) +; - /dev/poll (Solaris >= 7) +; - port (Solaris >= 10) +; Default Value: not set (auto detection) +; events.mechanism = epoll + ;;;;;;;;;;;;;;;;;;;; ; Pool Definitions ; ;;;;;;;;;;;;;;;;;;;; @@ -157,7 +167,8 @@ listen = 127.0.0.1:9000 ; Possible Values: ; static - a fixed number (pm.max_children) of child processes; ; dynamic - the number of child processes are set dynamically based on the -; following directives: +; following directives. With this process management, there will be +; always at least 1 children. ; pm.max_children - the maximum number of children that can ; be alive at the same time. ; pm.start_servers - the number of children created on startup. @@ -169,17 +180,23 @@ listen = 127.0.0.1:9000 ; state (waiting to process). If the number ; of 'idle' processes is greater than this ; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. ; Note: This value is mandatory. pm = dynamic ; The number of child processes to be created when pm is set to 'static' and the -; maximum number of child processes to be created when pm is set to 'dynamic'. +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. ; This value sets the limit on the number of simultaneous requests that will be ; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. ; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP ; CGI. The below defaults are based on a server without much resources. Don't ; forget to tweak pm.* to fit your needs. -; Note: Used when pm is set to either 'static' or 'dynamic' +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' ; Note: This value is mandatory. pm.max_children = 5 @@ -197,6 +214,11 @@ pm.min_spare_servers = 1 ; Note: Used only when pm is set to 'dynamic' ; Note: Mandatory when pm is set to 'dynamic' pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; ; The number of requests each child process should execute before respawning. ; This can be useful to work around memory leaks in 3rd party libraries. For @@ -207,7 +229,7 @@ pm.max_spare_servers = 3 ; The URI to view the FPM status page. If this value is not set, no URI will be ; recognized as a status page. It shows the following informations: ; pool - the name of the pool; -; process manager - static or dynamic; +; process manager - static, dynamic or ondemand; ; start time - the date and time FPM has started; ; start since - number of seconds since FPM has started; ; accepted conn - the number of request accepted by the pool; @@ -223,7 +245,7 @@ pm.max_spare_servers = 3 ; has started; ; max children reached - number of times, the process limit has been reached, ; when pm tries to start more children (works only for -; pm 'dynamic'); +; pm 'dynamic' and 'ondemand'); ; Value are updated in real time. ; Example output: ; pool: www