- Implemented FR #52569 (Add the "ondemand" process-manager to allow zero children)

This commit is contained in:
Jérôme Loyet 2011-10-08 21:04:10 +00:00
parent d64c8e78ca
commit 4e2664a602
24 changed files with 1994 additions and 72 deletions

View 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)
@ -446,6 +617,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)')

View File

@ -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 */

View File

@ -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 */

211
sapi/fpm/fpm/events/epoll.c Normal file
View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

276
sapi/fpm/fpm/events/poll.c Normal file
View File

@ -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 */

View File

@ -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 */

185
sapi/fpm/fpm/events/port.c Normal file
View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View 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);
}
/* }}} */

View 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));

View 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);

View File

@ -10,7 +10,6 @@
#include <string.h>
#include <php.h>
#include <php_network.h>
#include "fpm.h"
#include "fpm_process_ctl.h"
@ -23,13 +22,14 @@
#include "fpm_clock.h"
#include "fpm_log.h"
#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
#include "events/select.h"
#include "events/poll.h"
#include "events/epoll.h"
#include "events/devpoll.h"
#include "events/port.h"
#include "events/kqueue.h"
typedef struct fpm_event_queue_s {
struct fpm_event_queue_s *prev;
struct fpm_event_queue_s *next;
struct fpm_event_s *ev;
} fpm_event_queue;
#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
static void fpm_event_cleanup(int which, void *arg);
static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg);
@ -38,16 +38,12 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even
static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue);
static int fpm_event_nfds_max;
static struct fpm_event_module_s *module;
static struct fpm_event_queue_s *fpm_event_queue_timer = NULL;
static struct fpm_event_queue_s *fpm_event_queue_fd = NULL;
static php_pollfd *fpm_event_ufds = NULL;
static void fpm_event_cleanup(int which, void *arg) /* {{{ */
{
if (fpm_event_ufds) {
free(fpm_event_ufds);
}
fpm_event_queue_destroy(&fpm_event_queue_timer);
fpm_event_queue_destroy(&fpm_event_queue_fd);
}
@ -166,6 +162,11 @@ static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_even
}
*queue = elt;
/* ask the event module to add the fd from its own queue */
if (*queue == fpm_event_queue_fd && module->add) {
module->add(ev);
}
return 0;
}
/* }}} */
@ -189,6 +190,12 @@ static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_even
*queue = q->next;
(*queue)->prev = NULL;
}
/* ask the event module to remove the fd from its own queue */
if (*queue == fpm_event_queue_fd && module->remove) {
module->remove(ev);
}
free(q);
return 0;
}
@ -205,6 +212,11 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */
if (!queue) {
return;
}
if (*queue == fpm_event_queue_fd && module->clean) {
module->clean();
}
q = *queue;
while (q) {
tmp = q;
@ -216,27 +228,107 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */
}
/* }}} */
int fpm_event_init_main() /* {{{ */
int fpm_event_pre_init(char *machanism) /* {{{ */
{
struct fpm_worker_pool_s *wp;
/* 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 */
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);
/* kqueue */
module = fpm_event_kqueue_module();
if (module) {
if (!machanism || strcasecmp(module->name, machanism) == 0) {
return 0;
}
}
/* 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");
/* 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;
}
zlog(ZLOG_DEBUG, "%d fds have been reserved", fpm_event_nfds_max);
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 */
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) {
max += (wp->config->pm_max_children * 2);
}
}
if (module->init(max) < 0) {
zlog(ZLOG_ERROR, "Unable to initialize the event module %s", module->name);
return -1;
}
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;
}

View 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

View 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);
}
/* }}} */

View 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();

View 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;
}
/* }}} */

View 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,

View 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();

View File

@ -89,6 +89,16 @@
; Default Value: system defined value
;rlimit_core = 0
; Specify the event mechanism FPM will use. The following is available:
; - select (any POSIX os)
; - poll (any POSIX os)
; - epoll (linux >= 2.5.44)
; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
; - /dev/poll (Solaris >= 7)
; - port (Solaris >= 10)
; Default Value: not set (auto detection)
; events.mechanism = epoll
;;;;;;;;;;;;;;;;;;;;
; Pool Definitions ;
;;;;;;;;;;;;;;;;;;;;
@ -157,7 +167,8 @@ listen = 127.0.0.1:9000
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives:
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
@ -169,17 +180,23 @@ listen = 127.0.0.1:9000
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes to be created when pm is set to 'dynamic'.
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to either 'static' or 'dynamic'
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 5
@ -197,6 +214,11 @@ pm.min_spare_servers = 1
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 3
; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
;pm.process_idle_timeout = 10s;
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
@ -207,7 +229,7 @@ pm.max_spare_servers = 3
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following informations:
; pool - the name of the pool;
; process manager - static or dynamic;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
@ -223,7 +245,7 @@ pm.max_spare_servers = 3
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic');
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www