event.c revision 03880c49482316b8e564c1330e11dc0e654df341
135c1278adef96d36fd421c536b1acc54a341cfbsf/* Licensed to the Apache Software Foundation (ASF) under one or more
135c1278adef96d36fd421c536b1acc54a341cfbsf * contributor license agreements. See the NOTICE file distributed with
135c1278adef96d36fd421c536b1acc54a341cfbsf * this work for additional information regarding copyright ownership.
135c1278adef96d36fd421c536b1acc54a341cfbsf * The ASF licenses this file to You under the Apache License, Version 2.0
135c1278adef96d36fd421c536b1acc54a341cfbsf * (the "License"); you may not use this file except in compliance with
135c1278adef96d36fd421c536b1acc54a341cfbsf * the License. You may obtain a copy of the License at
135c1278adef96d36fd421c536b1acc54a341cfbsf * Unless required by applicable law or agreed to in writing, software
135c1278adef96d36fd421c536b1acc54a341cfbsf * distributed under the License is distributed on an "AS IS" BASIS,
135c1278adef96d36fd421c536b1acc54a341cfbsf * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135c1278adef96d36fd421c536b1acc54a341cfbsf * See the License for the specific language governing permissions and
135c1278adef96d36fd421c536b1acc54a341cfbsf * limitations under the License.
135c1278adef96d36fd421c536b1acc54a341cfbsf * This MPM tries to fix the 'keep alive problem' in HTTP.
135c1278adef96d36fd421c536b1acc54a341cfbsf * After a client completes the first request, the client can keep the
135c1278adef96d36fd421c536b1acc54a341cfbsf * connection open to send more requests with the same socket. This can save
135c1278adef96d36fd421c536b1acc54a341cfbsf * significant overhead in creating TCP connections. However, the major
135c1278adef96d36fd421c536b1acc54a341cfbsf * disadvantage is that Apache traditionally keeps an entire child
135c1278adef96d36fd421c536b1acc54a341cfbsf * process/thread waiting for data from the client. To solve this problem,
135c1278adef96d36fd421c536b1acc54a341cfbsf * this MPM has a dedicated thread for handling both the Listening sockets,
135c1278adef96d36fd421c536b1acc54a341cfbsf * and all sockets that are in a Keep Alive status.
135c1278adef96d36fd421c536b1acc54a341cfbsf * The MPM assumes the underlying apr_pollset implementation is somewhat
135c1278adef96d36fd421c536b1acc54a341cfbsf * threadsafe. This currently is only compatible with KQueue and EPoll. This
135c1278adef96d36fd421c536b1acc54a341cfbsf * enables the MPM to avoid extra high level locking or having to wake up the
135c1278adef96d36fd421c536b1acc54a341cfbsf * listener thread when a keep-alive socket needs to be sent to it.
135c1278adef96d36fd421c536b1acc54a341cfbsf * This MPM does not perform well on older platforms that do not have very good
135c1278adef96d36fd421c536b1acc54a341cfbsf * threading, like Linux with a 2.4 kernel, but this does not matter, since we
135c1278adef96d36fd421c536b1acc54a341cfbsf * require EPoll or KQueue.
135c1278adef96d36fd421c536b1acc54a341cfbsf * For FreeBSD, use 5.3. It is possible to run this MPM on FreeBSD 5.2.1, if
135c1278adef96d36fd421c536b1acc54a341cfbsf * you use libkse (see `man libmap.conf`).
135c1278adef96d36fd421c536b1acc54a341cfbsf * For NetBSD, use at least 2.0.
135c1278adef96d36fd421c536b1acc54a341cfbsf * For Linux, you should use a 2.6 kernel, and make sure your glibc has epoll
135c1278adef96d36fd421c536b1acc54a341cfbsf * support compiled in.
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc#error The Event MPM requires APR threads, but they are unavailable.
135c1278adef96d36fd421c536b1acc54a341cfbsf/* Limit on the total --- clients will be locked out if more servers than
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * this are needed. It is intended solely to keep the server from crashing
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * when things get out of hand.
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * We keep a hard maximum number of servers, for two reasons --- first off,
70ad0d9ff31ed8da58193eff4bf240247d1d6bddjailletc * in case something goes seriously wrong, we want to stop the fork bomb
135c1278adef96d36fd421c536b1acc54a341cfbsf * short of actually crashing the machine we're running on by filling some
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * kernel table. Secondly, it keeps the size of the scoreboard file small
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * enough that we can read the whole thing without worrying too much about
135c1278adef96d36fd421c536b1acc54a341cfbsf * the overhead.
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc/* Admin can't tune ServerLimit beyond MAX_SERVER_LIMIT. We want
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * some sort of compile-time limit to help catch typos.
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc/* Limit on the threads per process. Clients will be locked out if more than
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * this are needed.
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * We keep this for one reason it keeps the size of the scoreboard file small
135c1278adef96d36fd421c536b1acc54a341cfbsf * enough that we can read the whole thing without worrying too much about
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * the overhead.
135c1278adef96d36fd421c536b1acc54a341cfbsf/* Admin can't tune ThreadLimit beyond MAX_THREAD_LIMIT. We want
135c1278adef96d36fd421c536b1acc54a341cfbsf * some sort of compile-time limit to help catch typos.
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
cf9acd789f86df61f77c735ec3a45b0f5c88bacbjailletc * Actual definitions of config globals
#ifndef DEFAULT_WORKER_FACTOR
static int ap_daemons_to_start = 0;
static int min_spare_threads = 0;
static int max_spare_threads = 0;
static int ap_daemons_limit = 0;
static int max_workers = 0;
static int server_limit = 0;
static int thread_limit = 0;
static int had_healthy_child = 0;
static int dying = 0;
static int workers_may_exit = 0;
static int start_thread_may_exit = 0;
static int listener_may_exit = 0;
static int num_listensocks = 0;
static int resource_shortage = 0;
struct event_conn_state_t {
conn_rec *c;
apr_pool_t *p;
struct timeout_queue {
int count;
const char *tag;
(q).count++; \
(q).count--; \
#define TO_QUEUE_INIT(q) \
(q).tag = #q; \
#if HAVE_SERF
} s_baton_t;
int pid;
int tid;
int sd;
} proc_info;
int child_num_arg;
#if HAVE_SERF
, PT_SERF
, PT_USER
} poll_type_e;
void *baton;
void *user_baton;
int nsock;
int signaled;
typedef struct event_retained_data {
int first_server_limit;
int first_thread_limit;
int module_loads;
int sick_child_detected;
int maxclients_reported;
int max_daemons_limit;
int idle_spawn_rate;
#ifndef MAX_SPAWN_RATE
static int one_process = 0;
#ifdef DEBUG_SIGSTOP
int raise_sigstop_flags;
for (i = 0; i < num_listensocks; i++) {
for (i = 0; i < num_listensocks; i++)
static void close_worker_sockets(void)
for (i = 0; i < threads_per_child; i++) {
if (worker_sockets[i]) {
static void wakeup_listener(void)
if (!listener_os_thread) {
#ifdef HAVE_PTHREAD_KILL
#define ST_INIT 0
switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
case AP_MPMQ_IS_THREADED:
case AP_MPMQ_IS_FORKED:
case AP_MPMQ_IS_ASYNC:
case AP_MPMQ_HAS_SERF:
case AP_MPMQ_MAX_THREADS:
*result = 0;
*result = 0;
case AP_MPMQ_MAX_DAEMONS:
case AP_MPMQ_MPM_STATE:
case AP_MPMQ_GENERATION:
return OK;
static const char *event_get_name(void)
if (pchild) {
if (one_process) {
clean_child_exit(0);
static int child_fatal;
static int volatile shutdown_pending;
static int volatile restart_pending;
case CONN_STATE_LINGER_NORMAL:
case CONN_STATE_LINGER_SHORT:
case CONN_STATE_SUSPENDED:
return APR_SUCCESS;
static void set_signals(void)
#ifndef NO_USE_SIGACTION
if (!one_process) {
#ifndef NO_USE_SIGACTION
#ifdef AP_SIG_GRACEFUL_STOP
#ifdef SIGINT
#ifdef SIGXCPU
#ifdef SIGXFSZ
#ifdef SIGPIPE
if (!one_process) {
#ifdef SIGXCPU
#ifdef SIGXFSZ
#ifdef SIGHUP
#ifdef AP_SIG_GRACEFUL
#ifdef AP_SIG_GRACEFUL_STOP
#ifdef SIGPIPE
struct timeout_queue *q;
#ifdef AP_DEBUG
q = &short_linger_q;
q = &linger_q;
if (c->aborted
AP_DEBUG_ASSERT(0);
int my_thread_num)
conn_rec *c;
int rc;
apr_pool_clear(p);
cs->c = c;
cs->p = p;
c = cs->c;
if (!c->aborted) {
* fall thru to either wait for readability/timeout or
else if (c->data_in_output_filters) {
else if (c->data_in_input_filters) {
goto read_request;
apr_time_now();
static void check_infinite_requests(void)
if (ap_max_requests_per_child) {
if (!*closed) {
for (i = 0; i < threads_per_child; ++i) {
#if defined(SIGPROCMASK_SETS_THREAD_MASK)
#if HAVE_SERF
void *serf_baton)
void *serf_baton)
#if HAVE_SERF
#if HAVE_SERF
s_socket_remove, p);
return APR_SUCCESS;
return rc;
if (*have_idle_worker_p) {
if (blocking)
static int indexing_comp(void *a, void *b)
void *baton)
return APR_SUCCESS;
apr_pool_t *p,
int for_read,
void *baton)
int i = 0, nsock;
while(s[i] != NULL) {
nsock = i;
for (i = 0; i<nsock; i++) {
return final_rc;
int i = 0, nsock;
while(s[i] != NULL) {
nsock = i;
for (i = 0; i<nsock; i++) {
return final_rc;
struct timeout_queue *q;
int count = 0;
if (!q->count) {
count++;
if (!count)
while (count) {
count--;
int have_idle_worker = 0;
return NULL;
int workers_were_busy = 0;
if (listener_may_exit) {
if (conns_this_child <= 0)
if (te) {
#if HAVE_SERF
if (listener_may_exit) {
while (ep) {
while (num) {
blocking = 0;
if (!have_idle_worker) {
have_idle_worker = 0;
case CONN_STATE_LINGER_NORMAL:
case CONN_STATE_LINGER_SHORT:
ap_assert(0);
if (workers_were_busy) {
if (!listeners_disabled)
if (!listeners_disabled)
else if (listeners_disabled) {
listeners_disabled = 0;
if (!listeners_disabled) {
return NULL;
have_idle_worker = 0;
#if HAVE_SERF
out_pfd++;
num--;
listeners_disabled = 0;
return NULL;
/* XXX For ungraceful termination/restart, we definitely don't want to
int is_idle = 0;
while (!workers_may_exit) {
if (!is_idle) {
if (workers_may_exit) {
goto worker_pop;
else if (!workers_may_exit) {
is_idle = 0;
return NULL;
switch (signum) {
case SIGTERM:
case SIGINT:
int threads_created = 0;
int listener_started = 0;
int loops;
int prev_threads_created;
pchild);
for (i = 0; i < sizeof(good_methods) / sizeof(void*); i++) {
good_methods[i]);
* sizeof(apr_socket_t *));
for (i = 0; i < threads_per_child; i++) {
int status =
++loops;
return NULL;
if (listener) {
int iter;
iter = 0;
++iter;
for (i = 0; i < threads_per_child; i++) {
if (ap_max_requests_per_child) {
if (ap_thread_stacksize != 0) {
if (one_process) {
switch (terminate_mode) {
case ST_GRACEFUL:
case ST_UNGRACEFUL:
int pid;
if (one_process) {
set_signals();
if (!pid) {
#ifdef HAVE_BINDPROCESSOR
static void perform_idle_server_maintenance(void)
int idle_thread_count;
int free_length;
int totally_free_length = 0;
int last_non_dead;
int total_non_dead;
int active_thread_count = 0;
free_length = 0;
idle_thread_count = 0;
total_non_dead = 0;
for (i = 0; i < ap_daemons_limit; ++i) {
int any_dying_threads = 0;
int any_dead_threads = 0;
int child_threads_active = 0;
for (j = 0; j < threads_per_child; j++) {
if (any_dead_threads
if (all_dead_threads) {
++free_length;
if (!any_dying_threads) {
last_non_dead = i;
if (had_healthy_child) {
"or Min/MaxSpareThreads), "
for (i = 0; i < free_length; ++i) {
int child_slot;
if (child_slot < 0
if (child_slot >= 0) {
for (i = 0; i < threads_per_child; i++)
else if (remaining_children_to_start
status) == 0) {
else if (remaining_children_to_start) {
return DONE;
set_signals();
if (!child_fatal) {
return DONE;
} else if (shutdown_pending) {
int active_children;
int index;
if (!child_fatal) {
if (ap_graceful_shutdown_timeout) {
shutdown_pending = 0;
active_children = 0;
return DONE;
if (one_process) {
return DONE;
/* wake up the children...time to die. But we'll have more soon */
return OK;
int startup = 0;
int level_flags = 0;
pconf = p;
return DONE;
if (!one_process) {
return DONE;
return OK;
if (debug) {
no_detach = 0;
if (!retained) {
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
had_healthy_child = 0;
ap_extended_status = 0;
return OK;
int startup = 0;
if (startup) {
if (startup) {
if (startup) {
if (startup) {
if (startup) {
if (startup) {
if (startup) {
if (startup) {
if (startup) {
if (ap_daemons_to_start < 0) {
if (startup) {
if (startup) {
return OK;
one_process = 0;
const char *arg)
return err;
return NULL;
const char *arg)
return err;
return NULL;
const char *arg)
return err;
return NULL;
const char *arg)
return err;
return NULL;
const char *arg)
return err;
return NULL;
return err;
return NULL;
const char *arg)
return err;
return NULL;
const char *arg)
double val;
char *endptr;
return err;
if (*endptr)
if (val <= 0)
if (worker_factor == 0)
return NULL;
{NULL}