]> granicus.if.org Git - php/commitdiff
- Implemented FR #52569 (Add the "ondemand" process-manager to allow zero children)
authorJérôme Loyet <fat@php.net>
Sat, 8 Oct 2011 21:04:10 +0000 (21:04 +0000)
committerJérôme Loyet <fat@php.net>
Sat, 8 Oct 2011 21:04:10 +0000 (21:04 +0000)
26 files changed:
NEWS
sapi/fpm/Makefile.frag
sapi/fpm/config.m4
sapi/fpm/fpm/events/devpoll.c [new file with mode: 0644]
sapi/fpm/fpm/events/devpoll.h [new file with mode: 0644]
sapi/fpm/fpm/events/epoll.c [new file with mode: 0644]
sapi/fpm/fpm/events/epoll.h [new file with mode: 0644]
sapi/fpm/fpm/events/kqueue.c [new file with mode: 0644]
sapi/fpm/fpm/events/kqueue.h [new file with mode: 0644]
sapi/fpm/fpm/events/poll.c [new file with mode: 0644]
sapi/fpm/fpm/events/poll.h [new file with mode: 0644]
sapi/fpm/fpm/events/port.c [new file with mode: 0644]
sapi/fpm/fpm/events/port.h [new file with mode: 0644]
sapi/fpm/fpm/events/select.c [new file with mode: 0644]
sapi/fpm/fpm/events/select.h [new file with mode: 0644]
sapi/fpm/fpm/fpm_children.c
sapi/fpm/fpm/fpm_conf.c
sapi/fpm/fpm/fpm_conf.h
sapi/fpm/fpm/fpm_events.c
sapi/fpm/fpm/fpm_events.h
sapi/fpm/fpm/fpm_process_ctl.c
sapi/fpm/fpm/fpm_process_ctl.h
sapi/fpm/fpm/fpm_request.c
sapi/fpm/fpm/fpm_request.h
sapi/fpm/fpm/fpm_worker_pool.h
sapi/fpm/php-fpm.conf.in

diff --git a/NEWS b/NEWS
index bcb7b27b6a04a3dca2df6a494e6d7310ddefacd3..6439ab99d7242a98f31fa84ccf4dedd076de36cc 100644 (file)
--- 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
index 85a0ba70f9c46c1055bd457df7a13e093a278996..c5cea7e523d6ec92044d9ead0567b895b0302483 100644 (file)
@@ -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)
index 064493c602712662db0c4e75ace223546096b4b2..953fa1f7bcfc92746902845aa66747395e214c73 100644 (file)
@@ -366,6 +366,171 @@ AC_DEFUN([AC_FPM_TIMES],
 ])
 dnl }}}
 
+AC_DEFUN([AC_FPM_KQUEUE],
+[
+       AC_MSG_CHECKING([for kqueue])
+
+       AC_TRY_COMPILE(
+       [ 
+               #include <sys/types.h>
+               #include <sys/event.h>
+               #include <sys/time.h>
+       ], [
+               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 <port.h>
+       ], [
+               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 <stdio.h>
+               #include <sys/devpoll.h>
+       ], [
+               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 <sys/epoll.h>
+       ], [
+               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 <poll.h>
+       ], [
+               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 <sys/select.h>
+
+               /* According to earlier standards */
+               #include <sys/time.h>
+               #include <sys/types.h>
+               #include <unistd.h>
+       ], [
+               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 (file)
index 0000000..b3850cc
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_DEVPOLL
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/devpoll.h>
+#include <errno.h>
+
+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 (file)
index 0000000..add3b50
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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 (file)
index 0000000..8c95948
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_EPOLL
+
+#include <sys/epoll.h>
+#include <errno.h>
+
+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 (file)
index 0000000..15983e2
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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 (file)
index 0000000..e2591c6
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_KQUEUE
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+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 (file)
index 0000000..885de51
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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 (file)
index 0000000..f11f0b3
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_POLL
+
+#include <poll.h>
+#include <errno.h>
+#include <string.h>
+
+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 (file)
index 0000000..8eb525f
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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 (file)
index 0000000..2e4e553
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_PORT
+
+#include <port.h>
+#include <poll.h>
+#include <errno.h>
+
+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 (file)
index 0000000..4f3dc06
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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 (file)
index 0000000..7a50912
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_SELECT
+
+/* According to POSIX.1-2001 */
+#include <sys/select.h>
+
+/* According to earlier standards */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <errno.h>
+
+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, &current_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, &current_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 (file)
index 0000000..c1e6838
--- /dev/null
@@ -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 <jerome@loyet.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $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 */
index 383a75ddc8e2cfc8f4563d221c64f9f985ff0d9f..35058b0ea10ca9fba5577e8bc26bb2f88617df73 100644 (file)
@@ -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);
 }
 /* }}} */
index bcfe618b602d965f63cbe37ff674b2e8344e7d4a..82a45e5da690118afda18b6aa2267cb2c1bacccc 100644 (file)
@@ -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));
index 56556e95a44b8f55a5e3bba7162d2451e733f358..9fbd5064c06bcd9bab0cf9e80c2028b803a442e6 100644 (file)
@@ -8,7 +8,7 @@
 #include <stdint.h>
 #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);
index 1e95e1eef2f38f24128b5c2a28a430e38d0d983d..ab3a69df860ad60084c5c8d955c5ae8569c25c8c 100644 (file)
@@ -10,7 +10,6 @@
 #include <string.h>
 
 #include <php.h>
-#include <php_network.h>
 
 #include "fpm.h"
 #include "fpm_process_ctl.h"
 #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;
        }
 
index 586b24283565fc83ef046fd7df85841c578343e4..416ec6e728a030c1b9ad264fe8825515de88778a 100644 (file)
@@ -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
index 027b566bb221f19db342443217e406844c4b1728..5d80aedbdbe3d653f0d6fb50a1b96f8d5c6d86c7 100644 (file)
@@ -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);
+}
+/* }}} */
+
index 6bfcfd8c6beb1cfc0dd5c7e834e6cd27801ccc70..ad755e612a325f3209fcd47b3670990dce87769f 100644 (file)
@@ -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();
 
index f7cd815f64ed351b919f74dbd84e5e04115b7ea6..7b9e0f76796a7935071748f9eab858ca55905056 100644 (file)
@@ -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;
+}
+/* }}} */
index 05cca228b1948fec4b7a797124697edb5509a5ff..aebd36cff4f8e0b1c065c2d6d89f95ccdb994733 100644 (file)
@@ -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,
index 100c3689cb6ed86b9dcfa26bbd6e850350be7a29..6688e6d3ba2f0e1238a3f4cfda7b047045fdcaba 100644 (file)
@@ -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();
index 92738d308ca8778e80d32bd9030d86a2bdac7046..4e7952bdd2ef2796e798884df53e2906d8c5f4d9 100644 (file)
 ; 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