php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #52569
Patch fpm-ondemand.v11-5.3.patch revision 2011-07-14 22:38 UTC by fat@php.net
Patch fpm-ondemand.v11.patch revision 2011-07-14 22:27 UTC by fat@php.net
Patch fpm-ondemand.v10-5.3.patch revision 2011-07-10 17:49 UTC by fat@php.net
Patch fpm-ondemand.v10.patch revision 2011-07-10 17:49 UTC by fat@php.net
Patch fpm-ondemand.v9-5.3.patch revision 2011-07-09 12:30 UTC by fat@php.net
Patch fpm-ondemand.v9.patch revision 2011-07-09 12:30 UTC by fat@php.net
Patch fpm-ondemand.v8-5.3.patch revision 2011-07-09 00:22 UTC by fat@php.net
Patch fpm-ondemand.v8.patch revision 2011-07-09 00:21 UTC by fat@php.net
Patch fpm-ondemand.v7-5.3.patch revision 2011-07-05 23:12 UTC by fat@php.net
Patch fpm-ondemand.v7.patch revision 2011-07-05 23:08 UTC by fat@php.net
Patch fpm-ondemand-pm-v6 revision 2010-09-25 16:27 UTC by mplomer at gmx dot de
Patch php-fpm-ondemand-pm-v5 revision 2010-08-30 08:16 UTC by mplomer at gmx dot de
Patch fpm-ondemand.v4.patch revision 2010-08-27 06:27 UTC by fat@php.net
Patch fpm-ondemand-pm-v3 revision 2010-08-25 22:12 UTC by mplomer at gmx dot de
Patch fpm-ondemand.v2.patch.txt revision 2010-08-23 22:51 UTC by fat@php.net
Patch fpm-ondemand-pm-php53 revision 2010-08-10 18:01 UTC by mplomer at gmx dot de
Patch fpm-ondemand-pm revision 2010-08-09 20:14 UTC by fat@php.net

Patch fpm-ondemand.v11.patch for FPM related Bug #52569

Patch version 2011-07-14 22:27 UTC

Return to Bug #52569 | Download this patch
This patch renders other patches obsolete

Obsolete patches:

Patch Revisions:

Developer: fat@php.net

Index: sapi/fpm/php-fpm.conf.in
===================================================================
--- sapi/fpm/php-fpm.conf.in	(revision 313251)
+++ sapi/fpm/php-fpm.conf.in	(working copy)
@@ -82,6 +82,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 ; 
 ;;;;;;;;;;;;;;;;;;;;
@@ -150,7 +160,8 @@
 ; 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.
@@ -162,17 +173,23 @@
 ;                                    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
 
@@ -190,6 +207,11 @@
 ; 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
@@ -200,7 +222,7 @@
 ; 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;
@@ -216,7 +238,7 @@
 ;                          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
Index: sapi/fpm/fpm/fpm_request.h
===================================================================
--- sapi/fpm/fpm/fpm_request.h	(revision 313251)
+++ sapi/fpm/fpm/fpm_request.h	(working copy)
@@ -18,6 +18,7 @@
 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: sapi/fpm/fpm/fpm_process_ctl.c
===================================================================
--- sapi/fpm/fpm/fpm_process_ctl.c	(revision 313251)
+++ sapi/fpm/fpm/fpm_process_ctl.c	(working copy)
@@ -355,7 +355,24 @@
 			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_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: sapi/fpm/fpm/events/kqueue.c
===================================================================
--- sapi/fpm/fpm/events/kqueue.c	(revision 0)
+++ sapi/fpm/fpm/events/kqueue.c	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/select.c
===================================================================
--- sapi/fpm/fpm/events/select.c	(revision 0)
+++ sapi/fpm/fpm/events/select.c	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/epoll.h
===================================================================
--- sapi/fpm/fpm/events/epoll.h	(revision 0)
+++ sapi/fpm/fpm/events/epoll.h	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/kqueue.h
===================================================================
--- sapi/fpm/fpm/events/kqueue.h	(revision 0)
+++ sapi/fpm/fpm/events/kqueue.h	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/port.c
===================================================================
--- sapi/fpm/fpm/events/port.c	(revision 0)
+++ sapi/fpm/fpm/events/port.c	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/select.h
===================================================================
--- sapi/fpm/fpm/events/select.h	(revision 0)
+++ sapi/fpm/fpm/events/select.h	(revision 0)
@@ -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: sapi/fpm/fpm/events/devpoll.c
===================================================================
--- sapi/fpm/fpm/events/devpoll.c	(revision 0)
+++ sapi/fpm/fpm/events/devpoll.c	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/poll.c
===================================================================
--- sapi/fpm/fpm/events/poll.c	(revision 0)
+++ sapi/fpm/fpm/events/poll.c	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/port.h
===================================================================
--- sapi/fpm/fpm/events/port.h	(revision 0)
+++ sapi/fpm/fpm/events/port.h	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/devpoll.h
===================================================================
--- sapi/fpm/fpm/events/devpoll.h	(revision 0)
+++ sapi/fpm/fpm/events/devpoll.h	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/poll.h
===================================================================
--- sapi/fpm/fpm/events/poll.h	(revision 0)
+++ sapi/fpm/fpm/events/poll.h	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/events/epoll.c
===================================================================
--- sapi/fpm/fpm/events/epoll.c	(revision 0)
+++ sapi/fpm/fpm/events/epoll.c	(revision 0)
@@ -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 */
Index: sapi/fpm/fpm/fpm_status.c
===================================================================
--- sapi/fpm/fpm/fpm_status.c	(revision 313251)
+++ sapi/fpm/fpm/fpm_status.c	(working copy)
@@ -352,7 +352,7 @@
 		now_epoch = time(NULL);
 		spprintf(&buffer, 0, short_syntax,
 				scoreboard.pool,
-				scoreboard.pm == PM_STYLE_STATIC ? "static" : "dynamic",
+				scoreboard.pm == PM_STYLE_STATIC ? "static" : (scoreboard.pm == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand"),
 				time_buffer,
 				now_epoch - scoreboard.start_epoch,
 				scoreboard.requests,
Index: sapi/fpm/fpm/fpm_conf.c
===================================================================
--- sapi/fpm/fpm/fpm_conf.c	(revision 313251)
+++ sapi/fpm/fpm/fpm_conf.c	(working copy)
@@ -43,16 +43,18 @@
 #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")
 #define BOOL2STR(a) (a ? "yes" : "no")
-#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : "dynamic")
+#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : (a == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand"))
 #define GO(field) offsetof(struct fpm_global_config_s, field)
 #define WPO(field) offsetof(struct fpm_worker_pool_config_s, field)
 
 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);
@@ -66,7 +68,7 @@
 struct fpm_global_config_s fpm_global_config = {
 	.daemonize = 1,
 #ifdef HAVE_SYSLOG_H
-	.syslog_facility = -1
+	.syslog_facility = -1,
 #endif
 };
 static struct fpm_worker_pool_s *current_wp = NULL;
@@ -89,6 +91,7 @@
 #endif
 	{ "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 }
 };
 
@@ -116,6 +119,7 @@
 	{ "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.status_path",            &fpm_conf_set_string,      WPO(pm_status_path) },
 	{ "ping.path",                 &fpm_conf_set_string,      WPO(ping_path) },
 	{ "ping.response",             &fpm_conf_set_string,      WPO(ping_response) },
@@ -223,6 +227,22 @@
 }
 /* }}} */
 
+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);
@@ -475,8 +495,10 @@
 		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;
 }
@@ -542,6 +564,7 @@
 
 	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;
@@ -702,8 +725,8 @@
 			return -1;
 		}
 
-		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;
 		}
 
@@ -744,7 +767,28 @@
 				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;
 		}
 
 		if (wp->config->slowlog && *wp->config->slowlog) {
@@ -1041,6 +1085,10 @@
 		return -1;
 	}
 
+	if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) {
+		return -1;
+	}
+
 	if (0 > fpm_conf_process_all_pools()) {
 		return -1;
 	}
@@ -1063,6 +1111,7 @@
 {
 	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
@@ -1398,6 +1447,7 @@
 	zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold);
 	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) {
@@ -1421,6 +1471,7 @@
 		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.status_path = %s",             STR2STR(wp->config->pm_status_path));
 		zlog(ZLOG_NOTICE, "\tping.path = %s",                  STR2STR(wp->config->ping_path));
 		zlog(ZLOG_NOTICE, "\tping.response = %s",              STR2STR(wp->config->ping_response));
Index: sapi/fpm/fpm/fpm_process_ctl.h
===================================================================
--- sapi/fpm/fpm/fpm_process_ctl.h	(revision 313251)
+++ sapi/fpm/fpm/fpm_process_ctl.h	(working copy)
@@ -22,6 +22,7 @@
 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: sapi/fpm/fpm/fpm_conf.h
===================================================================
--- sapi/fpm/fpm/fpm_conf.h	(revision 313251)
+++ sapi/fpm/fpm/fpm_conf.h	(working copy)
@@ -32,6 +32,7 @@
 #endif
 	int rlimit_files;
 	int rlimit_core;
+	char *events_mechanism;
 };
 
 extern struct fpm_global_config_s fpm_global_config;
@@ -56,6 +57,7 @@
 	int pm_start_servers;
 	int pm_min_spare_servers;
 	int pm_max_spare_servers;
+	int pm_process_idle_timeout;
 	char *ping_path;
 	char *ping_response;
 	char *access_log;
@@ -80,7 +82,8 @@
 
 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: sapi/fpm/fpm/fpm_events.c
===================================================================
--- sapi/fpm/fpm/fpm_events.c	(revision 313251)
+++ sapi/fpm/fpm/fpm_events.c	(working copy)
@@ -10,7 +10,6 @@
 #include <string.h>
 
 #include <php.h>
-#include <php_network.h>
 
 #include "fpm.h"
 #include "fpm_process_ctl.h"
@@ -23,14 +22,15 @@
 #include "fpm_clock.h"
 #include "fpm_log.h"
 
+#include "events/select.h"
+#include "events/poll.h"
+#include "events/epoll.h"
+#include "events/devpoll.h"
+#include "events/port.h"
+#include "events/kqueue.h"
+
 #define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
 
-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;
-
 static void fpm_event_cleanup(int which, void *arg);
 static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg);
 static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev);
@@ -38,16 +38,12 @@
 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 @@
 	}
 	*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 @@
 				*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 @@
 	if (!queue) {
 		return;
 	}
+
+	if (*queue == fpm_event_queue_fd && module->clean) {
+		module->clean();
+	}
+
 	q = *queue;
 	while (q) {
 		tmp = q;
@@ -216,27 +228,107 @@
 }
 /* }}} */
 
+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, "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 @@
 		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 @@
 			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_WARNING, "php_poll2() returns %d", errno);
-			}
-		} 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_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: sapi/fpm/fpm/fpm_worker_pool.h
===================================================================
--- sapi/fpm/fpm/fpm_worker_pool.h	(revision 313251)
+++ sapi/fpm/fpm/fpm_worker_pool.h	(working copy)
@@ -38,6 +38,10 @@
 	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: sapi/fpm/fpm/fpm_events.h
===================================================================
--- sapi/fpm/fpm/fpm_events.h	(revision 313251)
+++ sapi/fpm/fpm/fpm_events.h	(working copy)
@@ -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 @@
 	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: sapi/fpm/fpm/fpm_request.c
===================================================================
--- sapi/fpm/fpm/fpm_request.c	(revision 313251)
+++ sapi/fpm/fpm/fpm_request.c	(working copy)
@@ -295,3 +295,20 @@
 	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: sapi/fpm/fpm/fpm_children.c
===================================================================
--- sapi/fpm/fpm/fpm_children.c	(revision 313251)
+++ sapi/fpm/fpm/fpm_children.c	(working copy)
@@ -370,6 +370,12 @@
 		} 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;
 	}
@@ -413,6 +419,22 @@
 
 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: sapi/fpm/config.m4
===================================================================
--- sapi/fpm/config.m4	(revision 313251)
+++ sapi/fpm/config.m4	(working copy)
@@ -366,7 +366,172 @@
 ])
 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
   AC_MSG_RESULT($PHP_FPM)
@@ -379,6 +544,12 @@
   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)
@@ -446,6 +617,12 @@
     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)')
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 17:01:58 2024 UTC