* The global list of ap_listen_rec structures
*/
AP_DECLARE_DATA extern ap_listen_rec *ap_listeners;
-
-AP_DECLARE_DATA extern ap_listen_rec **mpm_listen;
-
-AP_DECLARE_DATA extern int enable_default_listener;
-
-AP_DECLARE_DATA extern int num_buckets;
-
-AP_DECLARE_DATA extern int have_so_reuseport;
+AP_DECLARE_DATA extern int ap_num_listen_buckets;
+AP_DECLARE_DATA extern int ap_have_so_reuseport;
/**
* Setup all of the defaults for the listener list
*/
AP_DECLARE(int) ap_setup_listeners(server_rec *s);
-/**This function duplicates ap_listeners.
- * @param s The global server_rec
+/**
+ * This function duplicates ap_listeners into multiple buckets when configured
+ * to (see ListenCoresBucketsRatio) and the platform supports it (eg. number of
+ * online CPU cores and SO_REUSEPORT available).
* @param p The config pool
- * @param num_buckets The total number of listener buckets.
-**/
-AP_DECLARE(apr_status_t) ap_duplicate_listeners(server_rec *s, apr_pool_t *p, int num_buckets);
-
+ * @param s The global server_rec
+ * @param buckets The array of listeners buckets.
+ * @param num_buckets The total number of listeners buckets (array size).
+ * @remark If the given *num_buckets is 0 (input), it will be computed
+ * according to the platform capacities, otherwise (positive) it
+ * will be preserved. The number of listeners duplicated will
+ * always match *num_buckets, be it computed or given.
+ */
+AP_DECLARE(apr_status_t) ap_duplicate_listeners(apr_pool_t *p, server_rec *s,
+ ap_listen_rec ***buckets,
+ int *num_buckets);
/**
* Loop through the global ap_listen_rec list and close each of the sockets.
*/
AP_DECLARE_NONSTD(void) ap_close_listeners(void);
+/**
+ * Loop through the given ap_listen_rec list and close each of the sockets.
+ * @param listener The listener to close.
+ */
+AP_DECLARE_NONSTD(void) ap_close_listeners_ex(ap_listen_rec *listeners);
+
/**
* FIXMEDOC
*/
* called.
*/
AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd, void *dummy, const char *arg);
+AP_DECLARE_NONSTD(const char *) ap_set_listencbratio(cmd_parms *cmd, void *dummy, const char *arg);
AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
int argc, char *const argv[]);
AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd, void *dummy,
#define LISTEN_COMMANDS \
AP_INIT_TAKE1("ListenBacklog", ap_set_listenbacklog, NULL, RSRC_CONF, \
"Maximum length of the queue of pending connections, as used by listen(2)"), \
+AP_INIT_TAKE1("ListenCoresBucketsRatio", ap_set_listencbratio, NULL, RSRC_CONF, \
+ "Ratio between the number of CPU cores (online) and the number of listeners buckets"), \
AP_INIT_TAKE_ARGV("Listen", ap_set_listener, NULL, RSRC_CONF, \
"A port number or a numeric IP address and a port number, and an optional protocol"), \
AP_INIT_TAKE1("SendBufferSize", ap_set_send_buffer_size, NULL, RSRC_CONF, \
* 20140627.6 (2.5.0-dev) Added ap_pcre_version_string(), AP_REG_PCRE_COMPILED
* and AP_REG_PCRE_LOADED to ap_regex.h.
* 20140627.7 (2.5.0-dev) Add listener bucket in scoreboard.h's process_score.
+ * 20140627.8 (2.5.0-dev) Add ap_set_listencbratio(), ap_close_listeners_ex(),
+ * ap_duplicate_listeners(), ap_num_listen_buckets and
+ * ap_have_so_reuseport to ap_listen.h.
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20140627
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 7 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 8 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
#include "http_log.h"
#include "mpm_common.h"
+#include <stdlib.h>
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
AP_DECLARE_DATA ap_listen_rec *ap_listeners = NULL;
-AP_DECLARE_DATA ap_listen_rec **mpm_listen = NULL;
-AP_DECLARE_DATA int enable_default_listener = 1;
-AP_DECLARE_DATA int num_buckets = 1;
-AP_DECLARE_DATA int have_so_reuseport = 0;
+/* Let ap_num_listen_buckets be global so that it can
+ * be printed by ap_log_common(), but keep the listeners
+ * buckets static since it is used only here to close them
+ * all (including duplicated) with ap_close_listeners().
+ */
+AP_DECLARE_DATA int ap_num_listen_buckets;
+static ap_listen_rec **ap_listen_buckets;
+
+/* Determine once, at runtime, whether or not SO_REUSEPORT
+ * is usable on this platform, and hence whether or not
+ * listeners can be duplicated (if configured).
+ */
+AP_DECLARE_DATA int ap_have_so_reuseport = -1;
static ap_listen_rec *old_listeners;
static int ap_listenbacklog;
+static int ap_listencbratio;
static int send_buffer_size;
static int receive_buffer_size;
#ifdef HAVE_SYSTEMD
ap_sock_disable_nagle(s);
#endif
-#ifdef SO_REUSEPORT
- {
- int thesock;
- apr_os_sock_get(&thesock, s);
- if (setsockopt(thesock, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(int)) < 0) {
- /* defined by not valid? */
- if (errno == ENOPROTOOPT) {
- have_so_reuseport = 0;
- } /* Check if SO_REUSEPORT is supported by the running Linux Kernel.*/
- else {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(02638)
- "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEPORT)",
- server->bind_addr);
- apr_socket_close(s);
- return errno;
- }
- }
- else {
- have_so_reuseport = 1;
- }
+#if defined(SO_REUSEPORT)
+ if (ap_have_so_reuseport) {
+ int thesock;
+ apr_os_sock_get(&thesock, s);
+ if (setsockopt(thesock, SOL_SOCKET, SO_REUSEPORT,
+ (void *)&one, sizeof(int)) < 0) {
+ stat = apr_get_netos_error();
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(02638)
+ "make_sock: for address %pI, apr_socket_opt_set: "
+ "(SO_REUSEPORT)",
+ server->bind_addr);
+ apr_socket_close(s);
+ return stat;
+ }
}
#endif
#endif
server->sd = s;
- server->active = enable_default_listener;
+ server->active = 1;
server->accept_func = NULL;
}
}
#endif
- if (make_sock(pool, lr, enable_default_listener) == APR_SUCCESS) {
+ if (make_sock(pool, lr, 1) == APR_SUCCESS) {
++num_open;
}
else {
}
/* close the old listeners */
- for (lr = old_listeners; lr; lr = next) {
- apr_socket_close(lr->sd);
- lr->active = 0;
- next = lr->next;
- }
+ ap_close_listeners_ex(old_listeners);
old_listeners = NULL;
#if AP_NONBLOCK_WHEN_MULTI_LISTEN
}
}
-
#ifdef HAVE_SYSTEMD
if (use_systemd) {
const char *userdata_key = "ap_open_systemd_listeners";
return num_listeners;
}
-AP_DECLARE(apr_status_t) ap_duplicate_listeners(server_rec *s, apr_pool_t *p,
- int num_buckets) {
+AP_DECLARE(apr_status_t) ap_duplicate_listeners(apr_pool_t *p, server_rec *s,
+ ap_listen_rec ***buckets,
+ int *num_buckets)
+{
+ static int warn_once;
int i;
apr_status_t stat;
int use_nonblock = 0;
ap_listen_rec *lr;
- mpm_listen = apr_palloc(p, sizeof(ap_listen_rec*) * num_buckets);
- for (i = 0; i < num_buckets; i++) {
+ if (*num_buckets < 1) {
+ *num_buckets = 1;
+ if (ap_listencbratio > 0) {
+#ifdef _SC_NPROCESSORS_ONLN
+ if (ap_have_so_reuseport) {
+ int num_online_cores = sysconf(_SC_NPROCESSORS_ONLN),
+ val = num_online_cores / ap_listencbratio;
+ if (val > 1) {
+ *num_buckets = val;
+ }
+ ap_log_perror(APLOG_MARK, APLOG_INFO, 0, p, APLOGNO(02819)
+ "Using %i listeners bucket(s) based on %i "
+ "online CPU cores and a ratio of %i",
+ *num_buckets, num_online_cores,
+ ap_listencbratio);
+ }
+ else
+#endif
+ if (!warn_once) {
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, p, APLOGNO(02820)
+ "ListenCoresBucketsRatio ignored without "
+ "SO_REUSEPORT and _SC_NPROCESSORS_ONLN "
+ "support: using a single listeners bucket");
+ warn_once = 1;
+ }
+ }
+ }
+
+ *buckets = apr_pcalloc(p, *num_buckets * sizeof(ap_listen_rec *));
+ (*buckets)[0] = ap_listeners;
+
+ for (i = 1; i < *num_buckets; i++) {
ap_listen_rec *last = NULL;
lr = ap_listeners;
while (lr) {
else
#endif
{
- duplr = apr_palloc(p, sizeof(ap_listen_rec));
+ duplr = apr_palloc(p, sizeof(ap_listen_rec));
duplr->slave = NULL;
duplr->protocol = apr_pstrdup(p, lr->protocol);
hostname = apr_pstrdup(p, lr->bind_addr->hostname);
apr_sockaddr_info_get(&sa, hostname, APR_UNSPEC, port, 0, p);
duplr->bind_addr = sa;
duplr->next = NULL;
- if ((stat = apr_socket_create(&duplr->sd, duplr->bind_addr->family,
- SOCK_STREAM, 0, p)) != APR_SUCCESS) {
+ stat = apr_socket_create(&duplr->sd, duplr->bind_addr->family,
+ SOCK_STREAM, 0, p);
+ if (stat != APR_SUCCESS) {
ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, p, APLOGNO(02640)
- "ap_duplicate_socket: for address %pI, "
+ "ap_duplicate_listeners: for address %pI, "
"cannot duplicate a new socket!",
duplr->bind_addr);
return stat;
}
#if AP_NONBLOCK_WHEN_MULTI_LISTEN
use_nonblock = (ap_listeners && ap_listeners->next);
- if ((stat = apr_socket_opt_set(duplr->sd, APR_SO_NONBLOCK, use_nonblock))
- != APR_SUCCESS) {
+ stat = apr_socket_opt_set(duplr->sd, APR_SO_NONBLOCK, use_nonblock);
+ if (stat != APR_SUCCESS) {
ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(02641)
"unable to control socket non-blocking status");
return stat;
ap_apply_accept_filter(p, duplr, s);
if (last == NULL) {
- mpm_listen[i] = last = duplr;
+ (*buckets)[i] = last = duplr;
}
else {
last->next = duplr;
lr = lr->next;
}
}
+
+ ap_listen_buckets = *buckets;
+ ap_num_listen_buckets = *num_buckets;
return APR_SUCCESS;
}
AP_DECLARE_NONSTD(void) ap_close_listeners(void)
{
- ap_listen_rec *lr;
int i;
- for (i = 0; i < num_buckets; i++) {
- for (lr = mpm_listen[i]; lr; lr = lr->next) {
- apr_socket_close(lr->sd);
- lr->active = 0;
- }
+
+ ap_close_listeners_ex(ap_listeners);
+
+ /* Start from index 1 since either ap_duplicate_listeners()
+ * was called and ap_listen_buckets[0] == ap_listeners, or
+ * it wasn't and ap_num_listen_buckets == 0.
+ */
+ for (i = 1; i < ap_num_listen_buckets; i++) {
+ ap_close_listeners_ex(ap_listen_buckets[i]);
+ }
+}
+
+AP_DECLARE_NONSTD(void) ap_close_listeners_ex(ap_listen_rec *listeners)
+{
+ ap_listen_rec *lr;
+ for (lr = listeners; lr; lr = lr->next) {
+ apr_socket_close(lr->sd);
+ lr->active = 0;
}
}
{
old_listeners = ap_listeners;
ap_listeners = NULL;
+ ap_listen_buckets = NULL;
+ ap_num_listen_buckets = 0;
ap_listenbacklog = DEFAULT_LISTENBACKLOG;
+ ap_listencbratio = 0;
+
+ /* Check once whether or not SO_REUSEPORT is supported. */
+ if (ap_have_so_reuseport < 0) {
+ /* This is limited to Linux with defined SO_REUSEPORT (ie. 3.9+) for
+ * now since the implementation evenly distributes connections accross
+ * all the listening threads/processes.
+ *
+ * *BSDs have SO_REUSEPORT too but with a different semantic: the first
+ * wildcard address bound socket or the last non-wildcard address bound
+ * socket will receive connections (no evenness garantee); the rest of
+ * the sockets bound to the same port will not.
+ * This can't (always) work for httpd.
+ *
+ * TODO: latests DragonFlyBSD's SO_REUSEPORT (seems to?) have the same
+ * semantic as Linux, so we may need HAVE_SO_REUSEPORT available from
+ * configure.in some day.
+ */
+#if defined(SO_REUSEPORT) && defined(__linux__)
+ apr_socket_t *sock;
+ if (apr_socket_create(&sock, APR_UNSPEC, SOCK_STREAM, 0,
+ ap_pglobal) == APR_SUCCESS) {
+ int thesock, on = 1;
+ apr_os_sock_get(&thesock, sock);
+ ap_have_so_reuseport = (setsockopt(thesock, SOL_SOCKET,
+ SO_REUSEPORT, (void *)&on,
+ sizeof(int)) == 0);
+ apr_socket_close(sock);
+ }
+ else
+#endif
+ ap_have_so_reuseport = 0;
+
+ }
}
AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
return NULL;
}
+AP_DECLARE_NONSTD(const char *) ap_set_listencbratio(cmd_parms *cmd,
+ void *dummy,
+ const char *arg)
+{
+ int b;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ b = atoi(arg);
+ if (b < 1) {
+ return "ListenCoresBucketsRatio must be > 0";
+ }
+
+ ap_listencbratio = b;
+ return NULL;
+}
+
AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd,
void *dummy,
const char *arg)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG , 0, s, APLOGNO(02639)
"Using SO_REUSEPORT: %s (%d)",
- have_so_reuseport ? "yes" : "no",
- num_buckets);
+ ap_have_so_reuseport ? "yes" : "no",
+ ap_num_listen_buckets);
}
AP_DECLARE(void) ap_remove_pid(apr_pool_t *p, const char *rel_fname)
} event_retained_data;
static event_retained_data *retained;
-#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
+typedef struct event_child_bucket {
+ ap_pod_t *pod;
+ ap_listen_rec *listeners;
+} event_child_bucket;
+static int num_buckets; /* Number of listeners buckets */
+static event_child_bucket *all_buckets, /* All listeners buckets */
+ *my_bucket; /* Current child bucket */
-static ap_pod_t **pod;
-static ap_pod_t *child_pod;
-static ap_listen_rec *child_listen;
+#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
/* The event MPM respects a couple of runtime flags that can aid
* in debugging. Setting the -DNO_DETACH flag will prevent the root process
}
}
-static void close_listeners(int process_slot, int *closed) {
+static void close_listeners(int process_slot, int *closed)
+{
if (!*closed) {
int i;
disable_listensocks(process_slot);
- ap_close_listeners();
+ ap_close_listeners_ex(my_bucket->listeners);
*closed = 1;
dying = 1;
ap_scoreboard_image->parent[process_slot].quiescing = 1;
TO_QUEUE_INIT(short_linger_q);
listener_pollfd = apr_palloc(p, sizeof(apr_pollfd_t) * num_listensocks);
- for (lr = child_listen; lr != NULL; lr = lr->next, i++) {
+ for (lr = my_bucket->listeners; lr != NULL; lr = lr->next, i++) {
apr_pollfd_t *pfd;
AP_DEBUG_ASSERT(i < num_listensocks);
pfd = &listener_pollfd[i];
apr_thread_t *start_thread_id;
apr_pool_t *pskip;
int i;
- ap_listen_rec *lr;
mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
* child initializes
ap_fatal_signal_child_setup(ap_server_conf);
apr_pool_create(&pchild, pconf);
- child_listen = mpm_listen[child_bucket];
- child_pod = pod[child_bucket];
-
/* close unused listeners and pods */
for (i = 0; i < num_buckets; i++) {
if (i != child_bucket) {
- lr = mpm_listen[i];
- while(lr) {
- apr_socket_close(lr->sd);
- lr->active = 0;
- lr = lr->next;
- }
- ap_mpm_podx_close(pod[i]);
+ ap_close_listeners_ex(all_buckets[i].listeners);
+ ap_mpm_podx_close(all_buckets[i].pod);
}
}
apr_signal(SIGTERM, dummy_signal_handler);
/* Watch for any messages from the parent over the POD */
while (1) {
- rv = ap_mpm_podx_check(child_pod);
+ rv = ap_mpm_podx_check(my_bucket->pod);
if (rv == AP_MPM_PODX_NORESTART) {
/* see if termination was triggered while we slept */
switch (terminate_mode) {
}
if (one_process) {
+ my_bucket = &all_buckets[0];
+
set_signals();
event_note_child_started(slot, getpid());
- child_main(0, 0);
+ child_main(slot, 0);
/* NOTREACHED */
ap_assert(0);
return -1;
}
if (!pid) {
+ my_bucket = &all_buckets[bucket];
+
#ifdef HAVE_BINDPROCESSOR
/* By default, AIX binds to a single processor. This bit unbinds
* children which will then bind to another CPU.
if (idle_thread_count > max_spare_threads / num_buckets) {
/* Kill off one child */
- ap_mpm_podx_signal(pod[child_bucket], AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_signal(all_buckets[child_bucket].pod,
+ AP_MPM_PODX_GRACEFUL);
retained->idle_spawn_rate[child_bucket] = 1;
}
else if (idle_thread_count < min_spare_threads / num_buckets) {
* Kill child processes, tell them to call child_exit, etc...
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
event_note_child_killed);
/* Close our listeners, and then ask our children to do same */
ap_close_listeners();
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_GRACEFUL);
}
ap_relieve_child_processes(event_note_child_killed);
* really dead.
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, event_note_child_killed);
" received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_GRACEFUL);
}
/* This is mostly for debugging... so that we know what is still
* pthreads are stealing signals from us left and right.
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
{
int startup = 0;
int level_flags = 0;
+ ap_listen_rec **listen_buckets;
apr_status_t rv;
int i;
level_flags |= APLOG_STARTUP;
}
- enable_default_listener = 0;
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
(startup ? NULL : s),
return DONE;
}
- enable_default_listener = 1;
- ap_duplicate_listeners(ap_server_conf, pconf, num_buckets);
-
- pod = apr_palloc(pconf, sizeof(ap_pod_t *) * num_buckets);
-
- if (!one_process) {
- for (i = 0; i < num_buckets; i++) {
- if ((rv = ap_mpm_podx_open(pconf, &pod[i]))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
- (startup ? NULL : s),
- "could not open pipe-of-death");
- return DONE;
- }
+ if (one_process) {
+ num_buckets = 1;
+ }
+ else if (!retained->is_graceful) { /* Preserve the number of buckets
+ on graceful restarts. */
+ num_buckets = 0;
+ }
+ if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
+ &listen_buckets, &num_buckets))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not duplicate listeners");
+ return DONE;
+ }
+ all_buckets = apr_pcalloc(pconf, num_buckets *
+ sizeof(event_child_bucket));
+ for (i = 0; i < num_buckets; i++) {
+ if (!one_process && /* no POD in one_process mode */
+ (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not open pipe-of-death");
+ return DONE;
}
+ all_buckets[i].listeners = listen_buckets[i];
}
+
/* for skiplist */
srand((unsigned int)apr_time_now());
return OK;
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
retained->max_daemons_limit = -1;
}
- if (!retained->is_graceful) {
- num_buckets = 1;
-#ifdef _SC_NPROCESSORS_ONLN
- if (have_so_reuseport) {
- int num_online_cores = sysconf(_SC_NPROCESSORS_ONLN);
- if (num_online_cores > 8) {
- num_buckets = num_online_cores / 8;
- }
- }
-#endif
- }
++retained->module_loads;
if (retained->module_loads == 2) {
/* test for correct operation of fdqueue */
} event_retained_data;
static event_retained_data *retained;
-#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
+typedef struct event_child_bucket {
+ ap_pod_t *pod;
+ ap_listen_rec *listeners;
+} event_child_bucket;
+static int num_buckets; /* Number of listeners buckets */
+static event_child_bucket *all_buckets, /* All listeners buckets */
+ *my_bucket; /* Current child bucket */
-static ap_pod_t **pod;
-static ap_pod_t *child_pod;
-static ap_listen_rec *child_listen;
+#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
/* The eventopt MPM respects a couple of runtime flags that can aid
* in debugging. Setting the -DNO_DETACH flag will prevent the root process
}
}
-static void close_listeners(int process_slot, int *closed) {
+static void close_listeners(int process_slot, int *closed)
+{
if (!*closed) {
int i;
disable_listensocks(process_slot);
- ap_close_listeners();
+ ap_close_listeners_ex(my_bucket->listeners);
*closed = 1;
dying = 1;
ap_scoreboard_image->parent[process_slot].quiescing = 1;
TO_QUEUE_INIT(short_linger_q);
listener_pollfd = apr_palloc(p, sizeof(apr_pollfd_t) * num_listensocks);
- for (lr = child_listen; lr != NULL; lr = lr->next, i++) {
+ for (lr = my_bucket->listeners; lr != NULL; lr = lr->next, i++) {
apr_pollfd_t *pfd;
AP_DEBUG_ASSERT(i < num_listensocks);
pfd = &listener_pollfd[i];
apr_thread_t *start_thread_id;
apr_pool_t *pskip;
int i;
- ap_listen_rec *lr;
mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
* child initializes
ap_fatal_signal_child_setup(ap_server_conf);
apr_pool_create(&pchild, pconf);
- child_listen = mpm_listen[child_bucket];
- child_pod = pod[child_bucket];
-
/* close unused listeners and pods */
for (i = 0; i < num_buckets; i++) {
if (i != child_bucket) {
- lr = mpm_listen[i];
- while(lr) {
- apr_socket_close(lr->sd);
- lr->active = 0;
- lr = lr->next;
- }
- ap_mpm_podx_close(pod[i]);
+ ap_close_listeners_ex(all_buckets[i].listeners);
+ ap_mpm_podx_close(all_buckets[i].pod);
}
}
apr_signal(SIGTERM, dummy_signal_handler);
/* Watch for any messages from the parent over the POD */
while (1) {
- rv = ap_mpm_podx_check(child_pod);
+ rv = ap_mpm_podx_check(my_bucket->pod);
if (rv == AP_MPM_PODX_NORESTART) {
/* see if termination was triggered while we slept */
switch (terminate_mode) {
}
if (one_process) {
+ my_bucket = &all_buckets[0];
+
set_signals();
event_note_child_started(slot, getpid());
- child_main(0, 0);
+ child_main(slot, 0);
/* NOTREACHED */
ap_assert(0);
return -1;
}
if (!pid) {
+ my_bucket = &all_buckets[bucket];
+
#ifdef HAVE_BINDPROCESSOR
/* By default, AIX binds to a single processor. This bit unbinds
* children which will then bind to another CPU.
if (idle_thread_count > max_spare_threads / num_buckets) {
/* Kill off one child */
- ap_mpm_podx_signal(pod[child_bucket], AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_signal(all_buckets[child_bucket].pod,
+ AP_MPM_PODX_GRACEFUL);
retained->idle_spawn_rate[child_bucket] = 1;
}
else if (idle_thread_count < min_spare_threads / num_buckets) {
* Kill child processes, tell them to call child_exit, etc...
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
event_note_child_killed);
/* Close our listeners, and then ask our children to do same */
ap_close_listeners();
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_GRACEFUL);
}
ap_relieve_child_processes(event_note_child_killed);
* really dead.
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, event_note_child_killed);
" received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_GRACEFUL);
}
/* This is mostly for debugging... so that we know what is still
* pthreads are stealing signals from us left and right.
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
{
int startup = 0;
int level_flags = 0;
+ ap_listen_rec **listen_buckets;
apr_status_t rv;
int i;
level_flags |= APLOG_STARTUP;
}
- enable_default_listener = 0;
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
(startup ? NULL : s),
return DONE;
}
- enable_default_listener = 1;
- ap_duplicate_listeners(ap_server_conf, pconf, num_buckets);
-
- pod = apr_palloc(pconf, sizeof(ap_pod_t *) * num_buckets);
-
- if (!one_process) {
- for (i = 0; i < num_buckets; i++) {
- if ((rv = ap_mpm_podx_open(pconf, &pod[i]))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
- (startup ? NULL : s),
- "could not open pipe-of-death");
- return DONE;
- }
+ if (one_process) {
+ num_buckets = 1;
+ }
+ else if (!retained->is_graceful) { /* Preserve the number of buckets
+ on graceful restarts. */
+ num_buckets = 0;
+ }
+ if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
+ &listen_buckets, &num_buckets))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not duplicate listeners");
+ return DONE;
+ }
+ all_buckets = apr_pcalloc(pconf, num_buckets *
+ sizeof(event_child_bucket));
+ for (i = 0; i < num_buckets; i++) {
+ if (!one_process && /* no POD in one_process mode */
+ (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not open pipe-of-death");
+ return DONE;
}
+ all_buckets[i].listeners = listen_buckets[i];
}
+
/* for skiplist */
srand((unsigned int)apr_time_now());
return OK;
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
retained->max_daemons_limit = -1;
}
- if (!retained->is_graceful) {
- num_buckets = 1;
-#ifdef _SC_NPROCESSORS_ONLN
- if (have_so_reuseport) {
- int num_online_cores = sysconf(_SC_NPROCESSORS_ONLN);
- if (num_online_cores > 8) {
- num_buckets = num_online_cores / 8;
- }
- }
-#endif
- }
++retained->module_loads;
if (retained->module_loads == 2) {
/* test for correct operation of fdqueue */
/* config globals */
-static apr_proc_mutex_t **accept_mutex;
static int ap_daemons_to_start=0;
static int ap_daemons_min_free=0;
static int ap_daemons_max_free=0;
static int ap_daemons_limit=0; /* MaxRequestWorkers */
static int server_limit = 0;
static int mpm_state = AP_MPMQ_STARTING;
-static ap_pod_t **pod;
-static ap_pod_t *child_pod;
-static apr_proc_mutex_t *child_mutex;
-static ap_listen_rec *child_listen;
-
/* data retained by prefork across load/unload of the module
* allocated on first call to pre-config hook; located on
} prefork_retained_data;
static prefork_retained_data *retained;
+typedef struct prefork_child_bucket {
+ ap_pod_t *pod;
+ ap_listen_rec *listeners;
+ apr_proc_mutex_t *mutex;
+} prefork_child_bucket;
+static int num_buckets; /* Number of listeners buckets */
+static prefork_child_bucket *all_buckets, /* All listeners buckets */
+ *my_bucket; /* Current child bucket */
+
#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
/* one_process --- debugging mode variable; can be set from the command line
prefork_note_child_killed(/* slot */ 0, 0, 0);
}
- ap_mpm_pod_close(child_pod);
+ ap_mpm_pod_close(my_bucket->pod);
chdir_for_gprof();
exit(code);
}
-static void accept_mutex_on(void)
+static apr_status_t accept_mutex_on(void)
{
- apr_status_t rv = apr_proc_mutex_lock(child_mutex);
+ apr_status_t rv = apr_proc_mutex_lock(my_bucket->mutex);
if (rv != APR_SUCCESS) {
const char *msg = "couldn't grab the accept mutex";
exit(APEXIT_CHILDFATAL);
}
}
+ return APR_SUCCESS;
}
-static void accept_mutex_off(void)
+static apr_status_t accept_mutex_off(void)
{
- apr_status_t rv = apr_proc_mutex_unlock(child_mutex);
+ apr_status_t rv = apr_proc_mutex_unlock(my_bucket->mutex);
if (rv != APR_SUCCESS) {
const char *msg = "couldn't release the accept mutex";
exit(APEXIT_CHILDFATAL);
}
}
+ return APR_SUCCESS;
}
/* On some architectures it's safe to do unserialized accept()s in the single
* when it's safe in the single Listen case.
*/
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-#define SAFE_ACCEPT(stmt) do {if (child_listen->next) {stmt;}} while(0)
+#define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS)
#else
-#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
+#define SAFE_ACCEPT(stmt) (stmt)
#endif
static int prefork_query(int query_code, int *result, apr_status_t *rv)
static void stop_listening(int sig)
{
mpm_state = AP_MPMQ_STOPPING;
- ap_close_listeners();
+ ap_close_listeners_ex(my_bucket->listeners);
/* For a graceful stop, we want the child to exit when done */
die_now = 1;
ap_my_pid = getpid();
requests_this_child = 0;
- child_listen = mpm_listen[child_bucket];
- child_mutex = accept_mutex[child_bucket];
- child_pod = pod[child_bucket];
-
ap_fatal_signal_child_setup(ap_server_conf);
/* Get a sub context for global allocations in this child, so that
/* close unused listeners and pods */
for (i = 0; i < num_buckets; i++) {
if (i != child_bucket) {
- lr = mpm_listen[i];
- while(lr) {
- apr_socket_close(lr->sd);
- lr->active = 0;
- lr = lr->next;
- }
- ap_mpm_pod_close(pod[i]);
+ ap_close_listeners_ex(all_buckets[i].listeners);
+ ap_mpm_pod_close(all_buckets[i].pod);
}
}
/* needs to be done before we switch UIDs so we have permissions */
ap_reopen_scoreboard(pchild, NULL, 0);
- lockfile = apr_proc_mutex_lockfile(child_mutex);
- status = apr_proc_mutex_child_init(&child_mutex,
- lockfile,
- pchild);
+ status = SAFE_ACCEPT(apr_proc_mutex_child_init(&my_bucket->mutex,
+ apr_proc_mutex_lockfile(my_bucket->mutex),
+ pchild));
if (status != APR_SUCCESS) {
+ lockfile = apr_proc_mutex_lockfile(my_bucket->mutex);
ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, APLOGNO(00155)
"Couldn't initialize cross-process lock in child "
"(%s) (%s)",
lockfile ? lockfile : "none",
- apr_proc_mutex_name(child_mutex));
+ apr_proc_mutex_name(my_bucket->mutex));
clean_child_exit(APEXIT_CHILDFATAL);
}
clean_child_exit(APEXIT_CHILDSICK); /* assume temporary resource issue */
}
- for (lr = child_listen, i = num_listensocks; i--; lr = lr->next) {
+ for (lr = my_bucket->listeners, i = num_listensocks; i--; lr = lr->next) {
apr_pollfd_t pfd = { 0 };
pfd.desc_type = APR_POLL_SOCKET;
if (num_listensocks == 1) {
/* There is only one listener record, so refer to that one. */
- lr = child_listen;
+ lr = my_bucket->listeners;
}
else {
/* multiple listening sockets - need to poll */
* while we were processing the connection or we are the lucky
* idle server process that gets to die.
*/
- if (ap_mpm_pod_check(child_pod) == APR_SUCCESS) { /* selected as idle? */
+ if (ap_mpm_pod_check(my_bucket->pod) == APR_SUCCESS) { /* selected as idle? */
die_now = 1;
}
else if (retained->my_generation !=
}
if (one_process) {
+ my_bucket = &all_buckets[0];
+
apr_signal(SIGHUP, sig_term);
/* Don't catch AP_SIG_GRACEFUL in ONE_PROCESS mode :) */
apr_signal(SIGINT, sig_term);
#endif
apr_signal(SIGTERM, sig_term);
prefork_note_child_started(slot, getpid());
- child_main(slot, bucket);
+ child_main(slot, 0);
/* NOTREACHED */
ap_assert(0);
return -1;
}
if (!pid) {
+ my_bucket = &all_buckets[bucket];
+
#ifdef HAVE_BINDPROCESSOR
/* by default AIX binds to a single processor
* this bit unbinds children which will then bind to another cpu
* while we were counting
*/
bucket_kill_child_record = (bucket_kill_child_record + 1) % num_buckets;
- ap_mpm_pod_signal(pod[bucket_kill_child_record]);
+ ap_mpm_pod_signal(all_buckets[bucket_kill_child_record].pod);
retained->idle_spawn_rate = 1;
}
else if (idle_count < ap_daemons_min_free) {
{
int index;
int remaining_children_to_start;
- apr_status_t rv;
int i;
ap_log_pid(pconf, ap_pid_fname);
- /* Initialize cross-process accept lock for each bucket*/
- accept_mutex = apr_palloc(_pconf, sizeof(apr_proc_mutex_t *) * num_buckets);
- for (i = 0; i < num_buckets; i++) {
- rv = ap_proc_mutex_create(&accept_mutex[i], NULL, AP_ACCEPT_MUTEX_TYPE, NULL,
- s, _pconf, 0);
- if (rv != APR_SUCCESS) {
- mpm_state = AP_MPMQ_STOPPING;
- return !OK;
- }
- }
-
if (!retained->is_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
mpm_state = AP_MPMQ_STOPPING;
ap_log_common(s);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00165)
"Accept mutex: %s (default: %s)",
- apr_proc_mutex_name(accept_mutex[0]),
+ apr_proc_mutex_name(all_buckets[0].mutex),
apr_proc_mutex_defname());
mpm_state = AP_MPMQ_RUNNING;
/* kill off the idle ones */
for (i = 0; i < num_buckets; i++) {
- ap_mpm_pod_killpg(pod[i], retained->max_daemons_limit);
+ ap_mpm_pod_killpg(all_buckets[i].pod, retained->max_daemons_limit);
}
/* Send SIGUSR1 to the active children */
/* kill off the idle ones */
for (i = 0; i < num_buckets; i++) {
- ap_mpm_pod_killpg(pod[i], retained->max_daemons_limit);
+ ap_mpm_pod_killpg(all_buckets[i].pod, retained->max_daemons_limit);
}
/* This is mostly for debugging... so that we know what is still
{
int startup = 0;
int level_flags = 0;
+ ap_listen_rec **listen_buckets;
apr_status_t rv;
int i;
level_flags |= APLOG_STARTUP;
}
- enable_default_listener = 0;
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
(startup ? NULL : s),
return DONE;
}
- enable_default_listener = 1;
- ap_duplicate_listeners(ap_server_conf, pconf, num_buckets);
-
- pod = apr_palloc(pconf, sizeof(ap_pod_t *) * num_buckets);
+ if (one_process) {
+ num_buckets = 1;
+ }
+ else if (!retained->is_graceful) { /* Preserve the number of buckets
+ on graceful restarts. */
+ num_buckets = 0;
+ }
+ if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
+ &listen_buckets, &num_buckets))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not duplicate listeners");
+ return DONE;
+ }
+ all_buckets = apr_pcalloc(pconf, num_buckets *
+ sizeof(prefork_child_bucket));
for (i = 0; i < num_buckets; i++) {
- if ((rv = ap_mpm_pod_open(pconf, &pod[i]))) {
+ if (!one_process && /* no POD in one_process mode */
+ (rv = ap_mpm_pod_open(pconf, &all_buckets[i].pod))) {
ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
(startup ? NULL : s),
"could not open pipe-of-death");
return DONE;
}
- }
+ /* Initialize cross-process accept lock when safe accept is needed */
+ if ((rv = SAFE_ACCEPT(ap_proc_mutex_create(&all_buckets[i].mutex, NULL,
+ AP_ACCEPT_MUTEX_TYPE, NULL,
+ s, pconf, 0)))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not create accept mutex");
+ return DONE;
+ }
+ all_buckets[i].listeners = listen_buckets[i];
+ }
return OK;
}
retained->max_daemons_limit = -1;
retained->idle_spawn_rate = 1;
}
- if (!retained->is_graceful) {
- num_buckets = 1;
-#ifdef _SC_NPROCESSORS_ONLN
- if (have_so_reuseport) {
- int num_online_cores = sysconf(_SC_NPROCESSORS_ONLN);
- if (num_online_cores > 8) {
- num_buckets = num_online_cores / 8;
- }
- }
-#endif
- }
++retained->module_loads;
if (retained->module_loads == 2) {
if (!one_process && !foreground) {
} worker_retained_data;
static worker_retained_data *retained;
+typedef struct worker_child_bucket {
+ ap_pod_t *pod;
+ ap_listen_rec *listeners;
+ apr_proc_mutex_t *mutex;
+} worker_child_bucket;
+static int num_buckets; /* Number of listeners buckets */
+static worker_child_bucket *all_buckets, /* All listeners buckets */
+ *my_bucket; /* Current child bucket */
+
#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
/* The structure used to pass unique initialization info to each thread */
#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
-static ap_pod_t **pod;
-static ap_pod_t *child_pod;
-
/* The worker MPM respects a couple of runtime flags that can aid
* in debugging. Setting the -DNO_DETACH flag will prevent the root process
* from detaching from its controlling terminal. Additionally, setting
static pid_t parent_pid;
static apr_os_thread_t *listener_os_thread;
-/* Locks for accept serialization */
-static apr_proc_mutex_t **accept_mutex;
-static apr_proc_mutex_t *child_mutex;
-static ap_listen_rec *child_listen;
-
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-#define SAFE_ACCEPT(stmt) (child_listen->next ? (stmt) : APR_SUCCESS)
+#define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS)
#else
#define SAFE_ACCEPT(stmt) (stmt)
#endif
clean_child_exit(APEXIT_CHILDSICK);
}
- for (lr = child_listen; lr != NULL; lr = lr->next) {
+ for (lr = my_bucket->listeners; lr != NULL; lr = lr->next) {
apr_pollfd_t pfd = { 0 };
pfd.desc_type = APR_POLL_SOCKET;
/* We've already decremented the idle worker count inside
* ap_queue_info_wait_for_idler. */
- if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(child_mutex)))
+ if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(my_bucket->mutex)))
!= APR_SUCCESS) {
if (!listener_may_exit) {
break; /* skip the lock release */
}
- if (!child_listen->next) {
+ if (!my_bucket->listeners->next) {
/* Only one listener, so skip the poll */
- lr = child_listen;
+ lr = my_bucket->listeners;
}
else {
while (!listener_may_exit) {
resource_shortage = 1;
signal_threads(ST_GRACEFUL);
}
- if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(child_mutex)))
+ if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(my_bucket->mutex)))
!= APR_SUCCESS) {
if (listener_may_exit) {
}
}
else {
- if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(child_mutex)))
+ if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(my_bucket->mutex)))
!= APR_SUCCESS) {
int level = APLOG_EMERG;
}
}
- ap_close_listeners();
+ ap_close_listeners_ex(my_bucket->listeners);
ap_queue_term(worker_queue);
dying = 1;
ap_scoreboard_image->parent[process_slot].quiescing = 1;
apr_threadattr_t *thread_attr;
apr_thread_t *start_thread_id;
int i;
- ap_listen_rec *lr;
mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
* child initializes
ap_fatal_signal_child_setup(ap_server_conf);
apr_pool_create(&pchild, pconf);
- child_listen = mpm_listen[child_bucket];
- child_mutex = accept_mutex[child_bucket];
- child_pod = pod[child_bucket];
-
/* close unused listeners and pods */
for (i = 0; i < num_buckets; i++) {
if (i != child_bucket) {
- lr = mpm_listen[i];
- while(lr) {
- apr_socket_close(lr->sd);
- lr->active = 0;
- lr = lr->next;
- }
- ap_mpm_podx_close(pod[i]);
+ ap_close_listeners_ex(all_buckets[i].listeners);
+ ap_mpm_podx_close(all_buckets[i].pod);
}
}
/*stuff to do before we switch id's, so we have permissions.*/
ap_reopen_scoreboard(pchild, NULL, 0);
- rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&child_mutex,
- apr_proc_mutex_lockfile(child_mutex),
- pchild));
+ rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&my_bucket->mutex,
+ apr_proc_mutex_lockfile(my_bucket->mutex),
+ pchild));
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00280)
"Couldn't initialize cross-process lock in child");
apr_signal(SIGTERM, dummy_signal_handler);
/* Watch for any messages from the parent over the POD */
while (1) {
- rv = ap_mpm_podx_check(child_pod);
+ rv = ap_mpm_podx_check(my_bucket->pod);
if (rv == AP_MPM_PODX_NORESTART) {
/* see if termination was triggered while we slept */
switch(terminate_mode) {
}
if (one_process) {
+ my_bucket = &all_buckets[0];
+
set_signals();
worker_note_child_started(slot, getpid());
- child_main(0, 0);
+ child_main(slot, 0);
/* NOTREACHED */
ap_assert(0);
return -1;
}
if (!pid) {
+ my_bucket = &all_buckets[bucket];
+
#ifdef HAVE_BINDPROCESSOR
/* By default, AIX binds to a single processor. This bit unbinds
* children which will then bind to another CPU.
if (idle_thread_count > max_spare_threads / num_buckets) {
/* Kill off one child */
- ap_mpm_podx_signal(pod[child_bucket], AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_signal(all_buckets[child_bucket].pod,
+ AP_MPM_PODX_GRACEFUL);
retained->idle_spawn_rate[child_bucket] = 1;
}
else if (idle_thread_count < min_spare_threads / num_buckets) {
static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
int remaining_children_to_start;
- apr_status_t rv;
int i;
ap_log_pid(pconf, ap_pid_fname);
- /* Initialize cross-process accept lock */
- accept_mutex = apr_palloc(_pconf, sizeof(apr_proc_mutex_t *) * num_buckets);
- for (i = 0; i < num_buckets; i++) {
- rv = ap_proc_mutex_create(&accept_mutex[i], NULL, AP_ACCEPT_MUTEX_TYPE, NULL,
- s, _pconf, 0);
- if (rv != APR_SUCCESS) {
- mpm_state = AP_MPMQ_STOPPING;
- return !OK;
- }
- }
-
if (!retained->is_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
mpm_state = AP_MPMQ_STOPPING;
ap_log_common(s);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00294)
"Accept mutex: %s (default: %s)",
- apr_proc_mutex_name(accept_mutex[0]),
+ apr_proc_mutex_name(all_buckets[0].mutex),
apr_proc_mutex_defname());
mpm_state = AP_MPMQ_RUNNING;
* Kill child processes, tell them to call child_exit, etc...
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
worker_note_child_killed);
/* Close our listeners, and then ask our children to do same */
ap_close_listeners();
+
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_GRACEFUL);
}
ap_relieve_child_processes(worker_note_child_killed);
* really dead.
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, worker_note_child_killed);
AP_SIG_GRACEFUL_STRING " received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_GRACEFUL);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_GRACEFUL);
}
/* This is mostly for debugging... so that we know what is still
* pthreads are stealing signals from us left and right.
*/
for (i = 0; i < num_buckets; i++) {
- ap_mpm_podx_killpg(pod[i], ap_daemons_limit, AP_MPM_PODX_RESTART);
+ ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit,
+ AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
{
int startup = 0;
int level_flags = 0;
+ ap_listen_rec **listen_buckets;
apr_status_t rv;
int i;
level_flags |= APLOG_STARTUP;
}
- enable_default_listener = 0;
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
(startup ? NULL : s),
return DONE;
}
- enable_default_listener = 1;
- ap_duplicate_listeners(ap_server_conf, pconf, num_buckets);
-
- pod = apr_palloc(pconf, sizeof(ap_pod_t *) * num_buckets);
- if (!one_process) {
- for (i = 0; i < num_buckets; i++) {
- if ((rv = ap_mpm_podx_open(pconf, &pod[i]))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
- (startup ? NULL : s),
- "could not open pipe-of-death");
- return DONE;
- }
+ if (one_process) {
+ num_buckets = 1;
+ }
+ else if (!retained->is_graceful) { /* Preserve the number of buckets
+ on graceful restarts. */
+ num_buckets = 0;
+ }
+ if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
+ &listen_buckets, &num_buckets))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not duplicate listeners");
+ return DONE;
+ }
+ all_buckets = apr_pcalloc(pconf, num_buckets *
+ sizeof(worker_child_bucket));
+ for (i = 0; i < num_buckets; i++) {
+ if (!one_process && /* no POD in one_process mode */
+ (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not open pipe-of-death");
+ return DONE;
+ }
+ /* Initialize cross-process accept lock when safe accept is needed */
+ if ((rv = SAFE_ACCEPT(ap_proc_mutex_create(&all_buckets[i].mutex, NULL,
+ AP_ACCEPT_MUTEX_TYPE, NULL,
+ s, pconf, 0)))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not create accept mutex");
+ return DONE;
}
+ all_buckets[i].listeners = listen_buckets[i];
}
+
return OK;
}
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
retained->max_daemons_limit = -1;
}
- if (!retained->is_graceful) {
- num_buckets = 1;
-#ifdef _SC_NPROCESSORS_ONLN
- if (have_so_reuseport) {
- int num_online_cores = sysconf(_SC_NPROCESSORS_ONLN);
- if (num_online_cores > 8) {
- num_buckets = num_online_cores / 8;
- }
- }
-#endif
- }
++retained->module_loads;
if (retained->module_loads == 2) {
if (!one_process && !foreground) {
apr_pool_t *p;
apr_size_t len;
ap_listen_rec *lp;
- int i;
/* create a temporary pool for the socket. pconf stays around too long */
rv = apr_pool_create(&p, pod->p);
* plain-HTTP, not SSL; using an SSL port would either be
* expensive to do correctly (performing a complete SSL handshake)
* or cause log spam by doing incorrectly (simply sending EOF). */
- for (i = 0; i < num_buckets; i++) {
- lp = mpm_listen[i];
- while (lp && lp->protocol && strcasecmp(lp->protocol, "http") != 0) {
- lp = lp->next;
- }
- if (!lp) {
- lp = mpm_listen[i];
- }
-
- rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054)
- "get socket to connect to listener");
- apr_pool_destroy(p);
- return rv;
- }
-
- /* on some platforms (e.g., FreeBSD), the kernel won't accept many
- * queued connections before it starts blocking local connects...
- * we need to keep from blocking too long and instead return an error,
- * because the MPM won't want to hold up a graceful restart for a
- * long time
- */
- rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055)
- "set timeout on socket to connect to listener");
- apr_socket_close(sock);
- apr_pool_destroy(p);
- return rv;
- }
+ lp = ap_listeners;
+ while (lp && lp->protocol && strcasecmp(lp->protocol, "http") != 0) {
+ lp = lp->next;
+ }
+ if (!lp) {
+ lp = ap_listeners;
+ }
- rv = apr_socket_connect(sock, lp->bind_addr);
- if (rv != APR_SUCCESS) {
- int log_level = APLOG_WARNING;
+ rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054)
+ "get socket to connect to listener");
+ apr_pool_destroy(p);
+ return rv;
+ }
- if (APR_STATUS_IS_TIMEUP(rv)) {
- /* probably some server processes bailed out already and there
- * is nobody around to call accept and clear out the kernel
- * connection queue; usually this is not worth logging
- */
- log_level = APLOG_DEBUG;
- }
+ /* on some platforms (e.g., FreeBSD), the kernel won't accept many
+ * queued connections before it starts blocking local connects...
+ * we need to keep from blocking too long and instead return an error,
+ * because the MPM won't want to hold up a graceful restart for a
+ * long time
+ */
+ rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055)
+ "set timeout on socket to connect to listener");
+ apr_socket_close(sock);
+ apr_pool_destroy(p);
+ return rv;
+ }
- ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056)
- "connect to listener on %pI", lp->bind_addr);
- apr_pool_destroy(p);
- return rv;
+ rv = apr_socket_connect(sock, lp->bind_addr);
+ if (rv != APR_SUCCESS) {
+ int log_level = APLOG_WARNING;
+
+ if (APR_STATUS_IS_TIMEUP(rv)) {
+ /* probably some server processes bailed out already and there
+ * is nobody around to call accept and clear out the kernel
+ * connection queue; usually this is not worth logging
+ */
+ log_level = APLOG_DEBUG;
}
- if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) {
- /* Send a TLS 1.0 close_notify alert. This is perhaps the
- * "least wrong" way to open and cleanly terminate an SSL
- * connection. It should "work" without noisy error logs if
- * the server actually expects SSLv3/TLSv1. With
- * SSLv23_server_method() OpenSSL's SSL_accept() fails
- * ungracefully on receipt of this message, since it requires
- * an 11-byte ClientHello message and this is too short. */
- static const unsigned char tls10_close_notify[7] = {
- '\x15', /* TLSPlainText.type = Alert (21) */
- '\x03', '\x01', /* TLSPlainText.version = {3, 1} */
- '\x00', '\x02', /* TLSPlainText.length = 2 */
- '\x01', /* Alert.level = warning (1) */
- '\x00' /* Alert.description = close_notify (0) */
- };
- data = (const char *)tls10_close_notify;
- len = sizeof(tls10_close_notify);
- }
- else /* ... XXX other request types here? */ {
- /* Create an HTTP request string. We include a User-Agent so
- * that adminstrators can track down the cause of the
- * odd-looking requests in their logs. A complete request is
- * used since kernel-level filtering may require that much
- * data before returning from accept(). */
- data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
- ap_get_server_description(),
- " (internal dummy connection)\r\n\r\n", NULL);
- len = strlen(data);
- }
+ ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056)
+ "connect to listener on %pI", lp->bind_addr);
+ apr_pool_destroy(p);
+ return rv;
+ }
- apr_socket_send(sock, data, &len);
- apr_socket_close(sock);
+ if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) {
+ /* Send a TLS 1.0 close_notify alert. This is perhaps the
+ * "least wrong" way to open and cleanly terminate an SSL
+ * connection. It should "work" without noisy error logs if
+ * the server actually expects SSLv3/TLSv1. With
+ * SSLv23_server_method() OpenSSL's SSL_accept() fails
+ * ungracefully on receipt of this message, since it requires
+ * an 11-byte ClientHello message and this is too short. */
+ static const unsigned char tls10_close_notify[7] = {
+ '\x15', /* TLSPlainText.type = Alert (21) */
+ '\x03', '\x01', /* TLSPlainText.version = {3, 1} */
+ '\x00', '\x02', /* TLSPlainText.length = 2 */
+ '\x01', /* Alert.level = warning (1) */
+ '\x00' /* Alert.description = close_notify (0) */
+ };
+ data = (const char *)tls10_close_notify;
+ len = sizeof(tls10_close_notify);
+ }
+ else /* ... XXX other request types here? */ {
+ /* Create an HTTP request string. We include a User-Agent so
+ * that adminstrators can track down the cause of the
+ * odd-looking requests in their logs. A complete request is
+ * used since kernel-level filtering may require that much
+ * data before returning from accept(). */
+ data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
+ ap_get_server_description(),
+ " (internal dummy connection)\r\n\r\n", NULL);
+ len = strlen(data);
}
+
+ apr_socket_send(sock, data, &len);
+ apr_socket_close(sock);
apr_pool_destroy(p);
return rv;