- Fixed bug #52501 (libevent made FPM crashed when forking -- libevent has been removed)

This commit is contained in:
Jérôme Loyet 2010-11-26 13:46:15 +00:00
parent 222e0bc0f6
commit f13f4bc872
14 changed files with 468 additions and 368 deletions

2
NEWS
View File

@ -261,6 +261,8 @@
- PHP-FPM SAPI:
. Fixed inconsistent backlog default value (-1) in FPM on many systems. (fat)
. Fixed bug #52501 (libevent made FPM crashed when forking -- libevent has
been removed). (fat)
. Fixed bug #52725 (gcc builtin atomic functions were sometimes used when they
were not available). (fat)
. Fixed bug #52693 (configuration file errors are not logged to stderr). (fat)

View File

@ -2,220 +2,9 @@ dnl
dnl $Id$
dnl
minimum_libevent_version="1.4.11"
PHP_ARG_ENABLE(fpm,,
[ --enable-fpm EXPERIMENTAL: Enable building of the fpm SAPI executable], no, no)
dnl libevent check function {{{
dnl @synopsis AC_LIB_EVENT([MINIMUM-VERSION])
dnl
dnl Test for the libevent library of a particular version (or newer).
dnl Source: http://svn.apache.org/repos/asf/incubator/thrift/trunk/aclocal/ax_lib_event.m4
dnl Modified: This file was modified for autoconf-2.13 and the PHP_ARG_WITH macro.
dnl
dnl If no path to the installed libevent is given, the macro will first try
dnl using no -I or -L flags, then searches under /usr, /usr/local, /opt,
dnl and /opt/libevent.
dnl If these all fail, it will try the $LIBEVENT_ROOT environment variable.
dnl
dnl This macro requires that #include <sys/types.h> works and defines u_char.
dnl
dnl This macro calls:
dnl AC_SUBST(LIBEVENT_CFLAGS)
dnl AC_SUBST(LIBEVENT_LIBS)
dnl
dnl And (if libevent is found):
dnl AC_DEFINE(HAVE_LIBEVENT)
dnl
dnl It also leaves the shell variables "success" and "ac_have_libevent"
dnl set to "yes" or "no".
dnl
dnl NOTE: This macro does not currently work for cross-compiling,
dnl but it can be easily modified to allow it. (grep "cross").
dnl
dnl @category InstalledPackages
dnl @category C
dnl @version 2007-09-12
dnl @license AllPermissive
dnl
dnl Copyright (C) 2009 David Reiss
dnl Copying and distribution of this file, with or without modification,
dnl are permitted in any medium without royalty provided the copyright
dnl notice and this notice are preserved.
AC_DEFUN([AC_LIB_EVENT_DO_CHECK],
[
# Save our flags.
CPPFLAGS_SAVED="$CPPFLAGS"
LDFLAGS_SAVED="$LDFLAGS"
LIBS_SAVED="$LIBS"
LD_LIBRARY_PATH_SAVED="$LD_LIBRARY_PATH"
# Set our flags if we are checking a specific directory.
if test -n "$ac_libevent_path" ; then
LIBEVENT_CPPFLAGS="-I$ac_libevent_path/include"
if test -z "$PHP_LIBDIR"; then
LIBEVENT_LDFLAGS="-L$ac_libevent_path/lib"
else
LIBEVENT_LDFLAGS="-L$ac_libevent_path/$PHP_LIBDIR"
fi
LD_LIBRARY_PATH="$ac_libevent_path/lib:$LD_LIBRARY_PATH"
else
LIBEVENT_CPPFLAGS=""
LIBEVENT_LDFLAGS=""
fi
# Required flag for libevent.
LIBEVENT_LIBS="-levent"
# Prepare the environment for compilation.
CPPFLAGS="$CPPFLAGS $LIBEVENT_CPPFLAGS"
LDFLAGS="$LDFLAGS $LIBEVENT_LDFLAGS"
LIBS="$LIBS $LIBEVENT_LIBS"
export CPPFLAGS
export LDFLAGS
export LIBS
export LD_LIBRARY_PATH
success=no
# Compile, link, and run the program. This checks:
# - event.h is available for including.
# - event_get_version() is available for linking.
# - The event version string is lexicographically greater
# than the required version.
AC_TRY_RUN([
#include <sys/types.h>
#include <event.h>
int main(int argc, char *argv[])
{
const char* lib_version = event_get_version();
const char* wnt_version = "$WANT_LIBEVENT_VERSION";
for (;;) {
/* If we reached the end of the want version. We have it. */
if (*wnt_version == '\0' || *wnt_version == '-') {
return 0;
}
/* If the want version continues but the lib version does not, */
/* we are missing a letter. We don't have it. */
if (*lib_version == '\0' || *lib_version == '-') {
return 1;
}
/* In the 1.4 version numbering style, if there are more digits */
/* in one version than the other, that one is higher. */
int lib_digits;
for (lib_digits = 0;
lib_version[lib_digits] >= '0' &&
lib_version[lib_digits] <= '9';
lib_digits++)
;
int wnt_digits;
for (wnt_digits = 0;
wnt_version[wnt_digits] >= '0' &&
wnt_version[wnt_digits] <= '9';
wnt_digits++)
;
if (lib_digits > wnt_digits) {
return 0;
}
if (lib_digits < wnt_digits) {
return 1;
}
/* If we have greater than what we want. We have it. */
if (*lib_version > *wnt_version) {
return 0;
}
/* If we have less, we don't. */
if (*lib_version < *wnt_version) {
return 1;
}
lib_version++;
wnt_version++;
}
return 0;
}
],[
success=yes
])
# Restore flags.
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
LIBS="$LIBS_SAVED"
LD_LIBRARY_PATH="$LD_LIBRARY_PATH_SAVED"
])
AC_DEFUN([AC_LIB_EVENT],
[
PHP_ARG_WITH(libevent-dir,,
[ --with-libevent-dir[=PATH] libevent install prefix, for fpm SAPI. (default: /usr/local)], /usr/local, yes)
if test "$PHP_LIBEVENT_DIR" != "no"; then
WANT_LIBEVENT_VERSION=ifelse([$1], ,1.2,$1)
AC_MSG_CHECKING(for libevent >= $WANT_LIBEVENT_VERSION install prefix)
libevent_prefix=$ac_default_prefix
if test $prefix != "NONE" -a $prefix != "" -a $prefix != "no" ; then
libevent_prefix=$prefix
fi
if test "$PHP_LIBEVENT_DIR" = "yes"; then
PHP_LIBEVENT_DIR=$libevent_prefix
fi
if test "$PHP_LIBEVENT_DIR" != "yes" && test "$PHP_LIBEVENT_DIR" != "/usr/local"; then
dnl don't try to be too smart, check only $PHP_LIBEVENT_DIR if specified
ac_libevent_path=$PHP_LIBEVENT_DIR
AC_LIB_EVENT_DO_CHECK
if test "$success" = "no"; then
AC_MSG_ERROR([Could not find libevent >= $WANT_LIBEVENT_VERSION in $PHP_LIBEVENT_DIR])
fi
else
dnl check default prefixes then
for ac_libevent_path in "" $PHP_LIBEVENT_DIR /usr /usr/local /opt /opt/local /opt/libevent ; do
AC_LIB_EVENT_DO_CHECK
if test "$success" = "yes"; then
break;
fi
done
fi
if test "$success" != "yes" ; then
AC_MSG_RESULT(no)
ac_have_libevent=no
AC_MSG_ERROR([libevent >= $WANT_LIBEVENT_VERSION could not be found])
else
AC_MSG_RESULT($ac_libevent_path)
ac_have_libevent=yes
AC_DEFINE(HAVE_LIBEVENT, 1, [define if libevent is available])
fi
LIBEVENT_LIBS="-levent"
if test -n "$ac_libevent_path"; then
LIBEVENT_CFLAGS="-I$ac_libevent_path/include"
LIBEVENT_LIBS="-L$ac_libevent_path/$PHP_LIBDIR $LIBEVENT_LIBS"
LIBEVENT_PATH="$ac_libevent_path/$PHP_LIBDIR"
fi
AC_SUBST(LIBEVENT_CFLAGS)
AC_SUBST(LIBEVENT_LIBS)
AC_SUBST(LIBEVENT_PATH)
else
AC_MSG_ERROR([FPM requires libevent >= $WANT_LIBEVENT_VERSION. Please specify libevent install prefix with --with-libevent-dir=yes])
fi
])
dnl }}}
dnl configure checks {{{
AC_DEFUN([AC_FPM_STDLIBS],
[
@ -555,22 +344,6 @@ AC_MSG_CHECKING(for FPM build)
if test "$PHP_FPM" != "no"; then
AC_MSG_RESULT($PHP_FPM)
AC_LIB_EVENT([$minimum_libevent_version])
dnl check libevent build
LD_LIBRARY_PATH_SAVED="$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH="$LIBEVENT_PATH:$LD_LIBRARY_PATH"
AC_MSG_CHECKING(whether libevent build works)
PHP_TEST_BUILD(event_init, [
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
AC_MSG_ERROR([build test failed. Please check the config.log for details])
], $LIBEVENT_LIBS)
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH_SAVED"
AC_FPM_STDLIBS
AC_FPM_PRCTL
AC_FPM_CLOCK
@ -619,10 +392,7 @@ if test "$PHP_FPM" != "no"; then
PHP_FPM_TRACE_FILES="fpm/fpm_trace.c fpm/fpm_trace_$fpm_trace_type.c"
fi
PHP_FPM_CFLAGS="$LIBEVENT_CFLAGS -I$abs_srcdir/sapi/fpm"
SAPI_EXTRA_LIBS="$LIBEVENT_LIBS"
PHP_SUBST(SAPI_EXTRA_LIBS)
PHP_FPM_CFLAGS="-I$abs_srcdir/sapi/fpm"
INSTALL_IT=":"
PHP_FPM_FILES="fpm/fastcgi.c \

View File

@ -23,7 +23,7 @@
struct fpm_globals_s fpm_globals;
int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf, struct event_base **base) /* {{{ */
int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf) /* {{{ */
{
fpm_globals.argc = argc;
fpm_globals.argv = argv;
@ -40,7 +40,7 @@ int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf, s
0 > fpm_children_init_main() ||
0 > fpm_sockets_init_main() ||
0 > fpm_worker_pool_init_main() ||
0 > fpm_event_init_main(base)) {
0 > fpm_event_init_main()) {
return -1;
}
@ -57,7 +57,7 @@ int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf, s
/* children: return listening socket
parent: never return */
int fpm_run(int *max_requests, struct event_base *base) /* {{{ */
int fpm_run(int *max_requests) /* {{{ */
{
struct fpm_worker_pool_s *wp;
@ -65,7 +65,7 @@ int fpm_run(int *max_requests, struct event_base *base) /* {{{ */
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
int is_parent;
is_parent = fpm_children_create_initial(wp, base);
is_parent = fpm_children_create_initial(wp);
if (!is_parent) {
goto run_child;
@ -73,7 +73,7 @@ int fpm_run(int *max_requests, struct event_base *base) /* {{{ */
}
/* run event loop forever */
fpm_event_loop(base);
fpm_event_loop();
run_child: /* only workers reach this point */

View File

@ -6,11 +6,9 @@
#define FPM_H 1
#include <unistd.h>
#include <sys/types.h> /* for event.h below */
#include <event.h>
int fpm_run(int *max_requests, struct event_base *base);
int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf, struct event_base **base);
int fpm_run(int *max_requests);
int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf);
struct fpm_globals_s {
pid_t parent_pid;

View File

@ -171,7 +171,7 @@ int fpm_children_free(struct fpm_child_s *child) /* {{{ */
}
/* }}} */
void fpm_children_bury(struct event_base *base) /* {{{ */
void fpm_children_bury() /* {{{ */
{
int status;
pid_t pid;
@ -201,13 +201,13 @@ void fpm_children_bury(struct event_base *base) /* {{{ */
} else if (WIFSIGNALED(status)) {
const char *signame = fpm_signal_names[WTERMSIG(status)];
const char *have_core = WCOREDUMP(status) ? " (core dumped)" : "";
const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
if (signame == NULL) {
signame = "";
}
snprintf(buf, sizeof(buf), "on signal %d %s%s", WTERMSIG(status), signame, have_core);
snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
/* if it's been killed because of dynamic process management
* don't restart it automaticaly
@ -277,19 +277,19 @@ void fpm_children_bury(struct event_base *base) /* {{{ */
zlog(ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload", fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval);
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET, base);
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
}
}
if (restart_child) {
fpm_children_make(wp, 1 /* in event loop */, 1, 0, base);
fpm_children_make(wp, 1 /* in event loop */, 1, 0);
if (fpm_globals.is_child) {
break;
}
}
} else {
zlog(ZLOG_ALERT, "oops, unknown child exited %s", buf);
zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s", pid, buf);
}
}
}
@ -340,15 +340,15 @@ static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
}
/* }}} */
static void fpm_parent_resources_use(struct fpm_child_s *child, struct event_base *base) /* {{{ */
static void fpm_parent_resources_use(struct fpm_child_s *child) /* {{{ */
{
fpm_shm_slots_parent_use_slot(child);
fpm_stdio_parent_use_pipes(child, base);
fpm_stdio_parent_use_pipes(child);
fpm_child_link(child);
}
/* }}} */
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug, struct event_base *base) /* {{{ */
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */
{
int enough = 0;
pid_t pid;
@ -378,12 +378,8 @@ int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to
switch (pid) {
case 0 :
event_reinit(base); /* reinitialize event base after fork() */
fpm_child_resources_use(child);
fpm_globals.is_child = 1;
if (in_event_loop) {
fpm_event_exit_loop(base);
}
fpm_child_init(wp);
return 0;
@ -398,7 +394,7 @@ int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to
default :
child->pid = pid;
fpm_clock_get(&child->started);
fpm_parent_resources_use(child, base);
fpm_parent_resources_use(child);
zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
}
@ -409,9 +405,9 @@ 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, struct event_base *base) /* {{{ */
int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */
{
return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1, base);
return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
}
/* }}} */

View File

@ -7,15 +7,15 @@
#include <sys/time.h>
#include <sys/types.h>
#include <event.h>
#include "fpm_worker_pool.h"
#include "fpm_events.h"
int fpm_children_create_initial(struct fpm_worker_pool_s *wp, struct event_base *base);
int fpm_children_create_initial(struct fpm_worker_pool_s *wp);
int fpm_children_free(struct fpm_child_s *child);
void fpm_children_bury(struct event_base *base);
void fpm_children_bury();
int fpm_children_init_main();
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug, struct event_base *base);
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug);
struct fpm_child_s;
@ -23,7 +23,7 @@ struct fpm_child_s {
struct fpm_child_s *prev, *next;
struct timeval started;
struct fpm_worker_pool_s *wp;
struct event ev_stdout, ev_stderr;
struct fpm_event_s ev_stdout, ev_stderr;
int shm_slot_i;
int fd_stdout, fd_stderr;
void (*tracer)(struct fpm_child_s *);

View File

@ -9,6 +9,9 @@
#include <stdlib.h> /* for putenv */
#include <string.h>
#include <php.h>
#include <php_network.h>
#include "fpm.h"
#include "fpm_process_ctl.h"
#include "fpm_events.h"
@ -17,19 +20,43 @@
#include "fpm_signals.h"
#include "fpm_children.h"
#include "zlog.h"
#include "fpm_clock.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);
static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
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_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) /* {{{ */
{
struct event_base *base = (struct event_base *)arg;
event_base_free(base);
if (fpm_event_ufds) {
free(fpm_event_ufds);
}
fpm_event_queue_destroy(&fpm_event_queue_timer);
fpm_event_queue_destroy(&fpm_event_queue_fd);
}
/* }}} */
static void fpm_got_signal(int fd, short ev, void *arg) /* {{{ */
static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
char c;
int res;
struct event_base *base = (struct event_base *)arg;
int fd = ev->fd;;
do {
do {
@ -46,22 +73,22 @@ static void fpm_got_signal(int fd, short ev, void *arg) /* {{{ */
switch (c) {
case 'C' : /* SIGCHLD */
zlog(ZLOG_DEBUG, "received SIGCHLD");
fpm_children_bury(base);
fpm_children_bury();
break;
case 'I' : /* SIGINT */
zlog(ZLOG_DEBUG, "received SIGINT");
zlog(ZLOG_NOTICE, "Terminating ...");
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET, base);
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
break;
case 'T' : /* SIGTERM */
zlog(ZLOG_DEBUG, "received SIGTERM");
zlog(ZLOG_NOTICE, "Terminating ...");
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET, base);
fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
break;
case 'Q' : /* SIGQUIT */
zlog(ZLOG_DEBUG, "received SIGQUIT");
zlog(ZLOG_NOTICE, "Finishing ...");
fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET, base);
fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET);
break;
case '1' : /* SIGUSR1 */
zlog(ZLOG_DEBUG, "received SIGUSR1");
@ -74,7 +101,7 @@ static void fpm_got_signal(int fd, short ev, void *arg) /* {{{ */
case '2' : /* SIGUSR2 */
zlog(ZLOG_DEBUG, "received SIGUSR2");
zlog(ZLOG_NOTICE, "Reloading in progress ...");
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET, base);
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
break;
}
@ -86,57 +113,335 @@ static void fpm_got_signal(int fd, short ev, void *arg) /* {{{ */
}
/* }}} */
int fpm_event_init_main(struct event_base **base) /* {{{ */
static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev) /* {{{ */
{
*base = event_base_new();
if (!ev) {
return NULL;
}
zlog(ZLOG_DEBUG, "libevent %s: using %s", event_get_version(), event_base_get_method(*base));
while (queue) {
if (queue->ev == ev) {
return ev;
}
queue = queue->next;
}
if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, *base)) {
return NULL;
}
/* }}} */
static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
{
struct fpm_event_queue_s *elt;
if (!queue || !ev) {
return -1;
}
if (fpm_event_queue_isset(*queue, ev)) {
return 0;
}
if (!(elt = malloc(sizeof(struct fpm_event_queue_s)))) {
zlog(ZLOG_SYSERROR, "malloc() failed");
return -1;
}
elt->prev = NULL;
elt->next = NULL;
elt->ev = ev;
if (*queue) {
(*queue)->prev = elt;
elt->next = *queue;
}
*queue = elt;
return 0;
}
/* }}} */
static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
{
struct fpm_event_queue_s *q;
if (!queue || !ev) {
return -1;
}
q = *queue;
while (q) {
if (q->ev == ev) {
if (q->prev) {
q->prev->next = q->next;
}
if (q->next) {
q->next->prev = q->prev;
}
if (q == *queue) {
*queue = q->next;
(*queue)->prev = NULL;
}
free(q);
return 0;
}
q = q->next;
}
return -1;
}
/* }}} */
static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */
{
struct fpm_event_queue_s *q, *tmp;
if (!queue) {
return;
}
q = *queue;
while (q) {
tmp = q;
q = q->next;
/* q->prev = NULL */
free(tmp);
}
*queue = NULL;
}
/* }}} */
int fpm_event_init_main() /* {{{ */
{
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);
}
}
/* 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");
return -1;
}
zlog(ZLOG_DEBUG, "%d fds have been reserved", fpm_event_nfds_max);
if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) {
return -1;
}
return 0;
}
/* }}} */
int fpm_event_loop(struct event_base *base) /* {{{ */
void fpm_event_loop() /* {{{ */
{
static struct event signal_fd_event;
static struct fpm_event_s signal_fd_event;
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
fpm_event_add(&signal_fd_event, 0);
/* add timers */
fpm_pctl_heartbeat(NULL, 0, NULL);
fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);
event_set(&signal_fd_event, fpm_signals_get_fd(), EV_PERSIST | EV_READ, &fpm_got_signal, base);
event_base_set(base, &signal_fd_event);
event_add(&signal_fd_event, 0);
fpm_pctl_heartbeat(-1, 0, base);
fpm_pctl_perform_idle_server_maintenance_heartbeat(-1, 0, base);
zlog(ZLOG_NOTICE, "ready to handle connections");
event_base_dispatch(base);
while (1) {
struct fpm_event_queue_s *q, *q2;
struct timeval ms;
struct timeval tmp;
struct timeval now;
unsigned long int timeout;
int i, ret;
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
fpm_clock_get(&now);
timerclear(&ms);
/* search in the timeout queue for the next timer to trigger */
q = fpm_event_queue_timer;
while (q) {
if (!timerisset(&ms)) {
ms = q->ev->timeout;
} else {
if (timercmp(&q->ev->timeout, &ms, <)) {
ms = q->ev->timeout;
}
}
q = q->next;
}
/* 1s timeout if none has been set */
if (!timerisset(&ms) || timercmp(&ms, &now, <) || timercmp(&ms, &now, ==)) {
timeout = 1000;
} else {
timersub(&ms, &now, &tmp);
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;
}
/* 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;
}
}
/* trigger timers */
q = fpm_event_queue_timer;
while (q) {
fpm_clock_get(&now);
if (q->ev) {
if (timercmp(&now, &q->ev->timeout, >) || timercmp(&now, &q->ev->timeout, ==)) {
fpm_event_fire(q->ev);
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
if (q->ev->flags & FPM_EV_PERSIST) {
fpm_event_set_timeout(q->ev, now);
} else { /* delete the event */
q2 = q;
if (q->prev) {
q->prev->next = q->next;
}
if (q->next) {
q->next->prev = q->prev;
}
if (q == fpm_event_queue_timer) {
fpm_event_queue_timer = q->next;
fpm_event_queue_timer->prev = NULL;
}
q = q->next;
free(q2);
continue;
}
}
}
q = q->next;
}
}
}
/* }}} */
void fpm_event_fire(struct fpm_event_s *ev) /* {{{ */
{
if (!ev || !ev->callback) {
return;
}
(*ev->callback)( (struct fpm_event_s *) ev, ev->which, ev->arg);
}
/* }}} */
int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */
{
if (!ev || !callback || fd < -1) {
return -1;
}
memset(ev, 0, sizeof(struct fpm_event_s));
ev->fd = fd;
ev->callback = callback;
ev->arg = arg;
ev->flags = flags;
return 0;
}
/* }}} */
int fpm_event_add(int fd, struct event_base *base, struct event *ev, void (*callback)(int, short, void *), void *arg) /* {{{ */
int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency) /* {{{ */
{
event_set(ev, fd, EV_PERSIST | EV_READ, callback, arg);
event_base_set(base, ev);
return event_add(ev, 0);
struct timeval now;
struct timeval tmp;
if (!ev) {
return -1;
}
ev->index = -1;
/* it's a triggered event on incoming data */
if (ev->flags & FPM_EV_READ) {
ev->which = FPM_EV_READ;
if (fpm_event_queue_add(&fpm_event_queue_fd, ev) != 0) {
return -1;
}
return 0;
}
/* it's a timer event */
ev->which = FPM_EV_TIMEOUT;
fpm_clock_get(&now);
if (frequency >= 1000) {
tmp.tv_sec = frequency / 1000;
tmp.tv_usec = (frequency % 1000) * 1000;
} else {
tmp.tv_sec = 0;
tmp.tv_usec = frequency * 1000;
}
ev->frequency = tmp;
fpm_event_set_timeout(ev, now);
if (fpm_event_queue_add(&fpm_event_queue_timer, ev) != 0) {
return -1;
}
return 0;
}
/* }}} */
int fpm_event_del(struct event *ev) /* {{{ */
int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
{
return event_del(ev);
if (fpm_event_queue_del(&fpm_event_queue_fd, ev) != 0) {
return -1;
}
if (fpm_event_queue_del(&fpm_event_queue_timer, ev) != 0) {
return -1;
}
return 0;
}
/* }}} */
void fpm_event_exit_loop(struct event_base *base) /* {{{ */
{
event_base_loopbreak(base);
}
/* }}} */
void fpm_event_fire(struct event *ev) /* {{{ */
{
(*ev->ev_callback)( (int) ev->ev_fd, (short) ev->ev_res, ev->ev_arg);
}
/* }}} */

View File

@ -5,12 +5,28 @@
#ifndef FPM_EVENTS_H
#define FPM_EVENTS_H 1
void fpm_event_exit_loop(struct event_base *base);
int fpm_event_loop(struct event_base *base);
int fpm_event_add(int fd, struct event_base *base, struct event *ev, void (*callback)(int, short, void *), void *arg);
int fpm_event_del(struct event *ev);
void fpm_event_fire(struct event *ev);
int fpm_event_init_main(struct event_base **base);
#define FPM_EV_TIMEOUT (1 << 0)
#define FPM_EV_READ (1 << 1)
#define FPM_EV_PERSIST (1 << 2)
#define fpm_event_set_timer(ev, flags, cb, arg) fpm_event_set((ev), -1, (flags), (cb), (arg))
struct fpm_event_s {
int fd; /* not set with FPM_EV_TIMEOUT */
struct timeval timeout; /* next time to trigger */
struct timeval frequency;
void (*callback)(struct fpm_event_s *, short, void *);
void *arg;
int flags;
int index; /* index of the fd in the ufds array */
short which; /* type of event */
};
void fpm_event_loop();
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);
#endif

View File

@ -168,7 +168,6 @@ typedef struct _php_cgi_globals_struct {
HashTable user_config_cache;
char *error_header;
char *fpm_config;
struct event_base *event_base;
} php_cgi_globals_struct;
/* {{{ user_config_cache
@ -1780,11 +1779,11 @@ consult the installation file that came with this distribution, or visit \n\
}
}
if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, test_conf, &CGIG(event_base))) {
if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, test_conf)) {
return FAILURE;
}
fcgi_fd = fpm_run(&max_requests, CGIG(event_base));
fcgi_fd = fpm_run(&max_requests);
parent = 0;
fcgi_set_is_fastcgi(1);

View File

@ -49,29 +49,18 @@ static void fpm_pctl_cleanup(int which, void *arg) /* {{{ */
}
/* }}} */
static struct event pctl_event;
static struct fpm_event_s pctl_event;
static void fpm_pctl_action(int fd, short which, void *arg) /* {{{ */
static void fpm_pctl_action(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
struct event_base *base = (struct event_base *)arg;
evtimer_del(&pctl_event);
memset(&pctl_event, 0, sizeof(pctl_event));
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT, base);
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
}
/* }}} */
static int fpm_pctl_timeout_set(int sec, struct event_base *base) /* {{{ */
static int fpm_pctl_timeout_set(int sec) /* {{{ */
{
struct timeval tv = { .tv_sec = sec, .tv_usec = 0 };
if (evtimer_initialized(&pctl_event)) {
evtimer_del(&pctl_event);
}
evtimer_set(&pctl_event, &fpm_pctl_action, base);
event_base_set(base, &pctl_event);
evtimer_add(&pctl_event, &tv);
fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
fpm_event_add(&pctl_event, sec * 1000);
return 0;
}
/* }}} */
@ -181,7 +170,7 @@ static void fpm_pctl_kill_all(int signo) /* {{{ */
}
/* }}} */
static void fpm_pctl_action_next(struct event_base *base) /* {{{ */
static void fpm_pctl_action_next() /* {{{ */
{
int sig, timeout;
@ -207,11 +196,11 @@ static void fpm_pctl_action_next(struct event_base *base) /* {{{ */
fpm_pctl_kill_all(sig);
fpm_signal_sent = sig;
fpm_pctl_timeout_set(timeout, base);
fpm_pctl_timeout_set(timeout);
}
/* }}} */
void fpm_pctl(int new_state, int action, struct event_base *base) /* {{{ */
void fpm_pctl(int new_state, int action) /* {{{ */
{
switch (action) {
case FPM_PCTL_ACTION_SET :
@ -243,7 +232,7 @@ void fpm_pctl(int new_state, int action, struct event_base *base) /* {{{ */
/* fall down */
case FPM_PCTL_ACTION_TIMEOUT :
fpm_pctl_action_next(base);
fpm_pctl_action_next();
break;
case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
fpm_pctl_action_last();
@ -259,14 +248,14 @@ int fpm_pctl_can_spawn_children() /* {{{ */
}
/* }}} */
int fpm_pctl_child_exited(struct event_base *base) /* {{{ */
int fpm_pctl_child_exited() /* {{{ */
{
if (fpm_state == FPM_PCTL_STATE_NORMAL) {
return 0;
}
if (!fpm_globals.running_children) {
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED, base);
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
}
return 0;
}
@ -318,7 +307,7 @@ static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */
}
/* }}} */
static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now, struct event_base *base) /* {{{ */
static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ */
{
struct fpm_worker_pool_s *wp;
@ -396,7 +385,7 @@ static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now, struct
}
wp->warn_max_children = 0;
fpm_children_make(wp, 1, children_to_fork, 1, base);
fpm_children_make(wp, 1, children_to_fork, 1);
/* if it's a child, stop here without creating the next event
* this event is reserved to the master process
@ -418,37 +407,40 @@ static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now, struct
}
/* }}} */
void fpm_pctl_heartbeat(int fd, short which, void *arg) /* {{{ */
void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
static struct event heartbeat;
struct timeval tv = { .tv_sec = 0, .tv_usec = 130000 };
static struct fpm_event_s heartbeat;
struct timeval now;
struct event_base *base = (struct event_base *)arg;
if (which == EV_TIMEOUT) {
evtimer_del(&heartbeat);
fpm_clock_get(&now);
fpm_pctl_check_request_timeout(&now);
if (fpm_globals.parent_pid != getpid()) {
return; /* sanity check */
}
evtimer_set(&heartbeat, &fpm_pctl_heartbeat, base);
event_base_set(base, &heartbeat);
evtimer_add(&heartbeat, &tv);
if (which == FPM_EV_TIMEOUT) {
fpm_clock_get(&now);
fpm_pctl_check_request_timeout(&now);
return;
}
/* first call without setting to initialize the timer */
fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_heartbeat, NULL);
fpm_event_add(&heartbeat, FPM_PCTL_HEARTBEAT);
}
/* }}} */
void fpm_pctl_perform_idle_server_maintenance_heartbeat(int fd, short which, void *arg) /* {{{ */
void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
static struct event heartbeat;
struct timeval tv = { .tv_sec = 0, .tv_usec = FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT };
static struct fpm_event_s heartbeat;
struct timeval now;
struct event_base *base = (struct event_base *)arg;
if (which == EV_TIMEOUT) {
evtimer_del(&heartbeat);
if (fpm_globals.parent_pid != getpid()) {
return; /* sanity check */
}
if (which == FPM_EV_TIMEOUT) {
fpm_clock_get(&now);
if (fpm_pctl_can_spawn_children()) {
fpm_pctl_perform_idle_server_maintenance(&now, base);
fpm_pctl_perform_idle_server_maintenance(&now);
/* if it's a child, stop here without creating the next event
* this event is reserved to the master process
@ -457,11 +449,12 @@ void fpm_pctl_perform_idle_server_maintenance_heartbeat(int fd, short which, voi
return;
}
}
return;
}
evtimer_set(&heartbeat, &fpm_pctl_perform_idle_server_maintenance_heartbeat, base);
event_base_set(base, &heartbeat);
evtimer_add(&heartbeat, &tv);
/* first call without setting which to initialize the timer */
fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_perform_idle_server_maintenance_heartbeat, NULL);
fpm_event_add(&heartbeat, FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT);
}
/* }}} */

View File

@ -5,18 +5,22 @@
#ifndef FPM_PROCESS_CTL_H
#define FPM_PROCESS_CTL_H 1
#include "fpm_events.h"
/* spawn max 32 children at once */
#define FPM_MAX_SPAWN_RATE (32)
/* 1s (in µs here) heatbeat for idle server maintenance */
#define FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT (1000000)
/* 1s (in ms) heartbeat for idle server maintenance */
#define FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT (1000)
/* 130ms heartbeat for pctl */
#define FPM_PCTL_HEARTBEAT (130)
struct fpm_child_s;
void fpm_pctl(int new_state, int action, struct event_base *base);
void fpm_pctl(int new_state, int action);
int fpm_pctl_can_spawn_children();
int fpm_pctl_kill(pid_t pid, int how);
void fpm_pctl_heartbeat(int fd, short which, void *arg);
void fpm_pctl_perform_idle_server_maintenance_heartbeat(int fd, short which, void *arg);
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);
int fpm_pctl_child_exited();
int fpm_pctl_init_main();

View File

@ -71,18 +71,30 @@ int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
}
/* }}} */
static void fpm_stdio_child_said(int fd, short which, void *arg) /* {{{ */
static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
static const int max_buf_size = 1024;
int fd = ev->fd;
char buf[max_buf_size];
struct fpm_child_s *child = arg;
int is_stdout = fd == child->fd_stdout;
struct event *ev = is_stdout ? &child->ev_stdout : &child->ev_stderr;
struct fpm_child_s *child;
int is_stdout;
struct fpm_event_s *event;
int fifo_in = 1, fifo_out = 1;
int is_last_message = 0;
int in_buf = 0;
int res;
if (!arg) {
return;
}
child = (struct fpm_child_s *)arg;
is_stdout = (fd == child->fd_stdout);
if (is_stdout) {
event = &child->ev_stdout;
} else {
event = &child->ev_stderr;
}
while (fifo_in || fifo_out) {
if (fifo_in) {
res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
@ -96,7 +108,7 @@ static void fpm_stdio_child_said(int fd, short which, void *arg) /* {{{ */
zlog(ZLOG_SYSERROR, "read() failed");
}
fpm_event_del(ev);
fpm_event_del(event);
is_last_message = 1;
if (is_stdout) {
@ -183,7 +195,7 @@ int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
}
/* }}} */
int fpm_stdio_parent_use_pipes(struct fpm_child_s *child, struct event_base *base) /* {{{ */
int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */
{
if (0 == child->wp->config->catch_workers_output) { /* not required */
return 0;
@ -195,8 +207,11 @@ int fpm_stdio_parent_use_pipes(struct fpm_child_s *child, struct event_base *bas
child->fd_stdout = fd_stdout[0];
child->fd_stderr = fd_stderr[0];
fpm_event_add(child->fd_stdout, base, &child->ev_stdout, fpm_stdio_child_said, child);
fpm_event_add(child->fd_stderr, base, &child->ev_stderr, fpm_stdio_child_said, child);
fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
fpm_event_add(&child->ev_stdout, 0);
fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
fpm_event_add(&child->ev_stderr, 0);
return 0;
}
/* }}} */

View File

@ -12,7 +12,7 @@ int fpm_stdio_init_final();
int fpm_stdio_init_child(struct fpm_worker_pool_s *wp);
int fpm_stdio_prepare_pipes(struct fpm_child_s *child);
void fpm_stdio_child_use_pipes(struct fpm_child_s *child);
int fpm_stdio_parent_use_pipes(struct fpm_child_s *child, struct event_base *base);
int fpm_stdio_parent_use_pipes(struct fpm_child_s *child);
int fpm_stdio_discard_pipes(struct fpm_child_s *child);
int fpm_stdio_open_error_log(int reopen);

View File

@ -268,6 +268,8 @@ pm.max_children = 50
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes