- 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)
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],
[
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
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 \
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;
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;
}
/* 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;
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;
}
/* run event loop forever */
- fpm_event_loop(base);
+ fpm_event_loop();
run_child: /* only workers reach this point */
#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;
}
/* }}} */
-void fpm_children_bury(struct event_base *base) /* {{{ */
+void fpm_children_bury() /* {{{ */
{
int status;
pid_t pid;
} 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
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);
}
}
}
}
/* }}} */
-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;
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;
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);
}
}
/* }}} */
-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);
}
/* }}} */
#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;
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 *);
#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"
#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 {
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");
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;
}
}
/* }}} */
-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) /* {{{ */
+{
+ if (!ev) {
+ return NULL;
+ }
+
+ while (queue) {
+ if (queue->ev == ev) {
+ return ev;
+ }
+ queue = queue->next;
+ }
+
+ 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() /* {{{ */
{
- *base = event_base_new();
+ 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);
+ }
+ }
- zlog(ZLOG_DEBUG, "libevent %s: using %s", event_get_version(), event_base_get_method(*base));
+ /* 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;
+ }
- if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, *base)) {
+ 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);
- return 0;
+
+ 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;
+ }
+ }
}
/* }}} */
-int fpm_event_add(int fd, struct event_base *base, struct event *ev, void (*callback)(int, short, void *), void *arg) /* {{{ */
+void fpm_event_fire(struct fpm_event_s *ev) /* {{{ */
{
- event_set(ev, fd, EV_PERSIST | EV_READ, callback, arg);
- event_base_set(base, ev);
- return event_add(ev, 0);
+ if (!ev || !ev->callback) {
+ return;
+ }
+
+ (*ev->callback)( (struct fpm_event_s *) ev, ev->which, ev->arg);
}
/* }}} */
-int fpm_event_del(struct event *ev) /* {{{ */
+int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */
{
- return event_del(ev);
+ 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;
}
/* }}} */
-void fpm_event_exit_loop(struct event_base *base) /* {{{ */
+int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency) /* {{{ */
{
- event_base_loopbreak(base);
+ 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;
}
/* }}} */
-void fpm_event_fire(struct event *ev) /* {{{ */
+int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
{
- (*ev->ev_callback)( (int) ev->ev_fd, (short) ev->ev_res, ev->ev_arg);
+ 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;
}
/* }}} */
+/* }}} */
#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
HashTable user_config_cache;
char *error_header;
char *fpm_config;
- struct event_base *event_base;
} php_cgi_globals_struct;
/* {{{ user_config_cache
}
}
- 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);
}
/* }}} */
-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;
}
/* }}} */
}
/* }}} */
-static void fpm_pctl_action_next(struct event_base *base) /* {{{ */
+static void fpm_pctl_action_next() /* {{{ */
{
int sig, timeout;
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 :
/* 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();
}
/* }}} */
-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;
}
}
/* }}} */
-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;
}
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
}
/* }}} */
-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);
+ if (fpm_globals.parent_pid != getpid()) {
+ return; /* sanity check */
+ }
+
+ if (which == FPM_EV_TIMEOUT) {
fpm_clock_get(&now);
fpm_pctl_check_request_timeout(&now);
+ return;
}
- evtimer_set(&heartbeat, &fpm_pctl_heartbeat, base);
- event_base_set(base, &heartbeat);
- evtimer_add(&heartbeat, &tv);
+ /* 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
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);
}
/* }}} */
#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();
}
/* }}} */
-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);
zlog(ZLOG_SYSERROR, "read() failed");
}
- fpm_event_del(ev);
+ fpm_event_del(event);
is_last_message = 1;
if (is_stdout) {
}
/* }}} */
-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;
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;
}
/* }}} */
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);
; 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