From: Jeff Trawick Date: Sat, 5 Mar 2011 13:36:09 +0000 (+0000) Subject: worker, event, prefork: Correct several issues when built as X-Git-Tag: 2.3.12~278 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4487fa925acb8ed6cbc8f785de5d91e411e66c3a;p=thirdparty%2Fapache%2Fhttpd.git worker, event, prefork: Correct several issues when built as DSOs; most notably, the scoreboard was reinitialized during graceful restart, such that processes of the previous generation were not observable. Other noticeable ones: * the generation number did not increment properly * worker's check-config hook didn't detect startup properly, so messages about inconsistent directives could have been sent to the wrong place max_daemons_limit wasn't tracked across restart, but as the scoreboard was always cleared this was a non-issue. Reinitialization of idle_spawn_rate, hold_off_on_exponential_spawning, and maxclients-message-reported may or may not have been features, but the big picture is that the behaviour shouldn't have differed. These changes should have been part of r758185. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1078286 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index b09f698e974..6930b277dd3 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.3.12 + *) worker, event, prefork: Correct several issues when built as + DSOs; most notably, the scoreboard was reinitialized during graceful + restart, such that processes of the previous generation were not + observable. [Jeff Trawick] Changes with Apache 2.3.11 diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index a66027a8fa9..fb38bf6f072 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -169,8 +169,6 @@ static int resource_shortage = 0; static fd_queue_t *worker_queue; static fd_queue_info_t *worker_queue_info; static int mpm_state = AP_MPMQ_STARTING; -static int sick_child_detected; -static ap_generation_t my_generation = 0; static apr_thread_mutex_t *timeout_mutex; APR_RING_HEAD(timeout_head_t, conn_state_t); @@ -230,19 +228,33 @@ typedef struct event_retained_data { int first_server_limit; int first_thread_limit; int module_loads; + int sick_child_detected; + ap_generation_t my_generation; + int volatile is_graceful; /* set from signal handler */ + int maxclients_reported; + /* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We + * use this value to optimize routines that have to scan the entire + * scoreboard. + */ + int max_daemons_limit; + /* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ + int idle_spawn_rate; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif + int hold_off_on_exponential_spawning; } event_retained_data; static event_retained_data *retained; #define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t) -/* - * The max child slot ever assigned, preserved across restarts. Necessary - * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We - * use this value to optimize routines that have to scan the entire - * scoreboard. - */ -static int max_daemons_limit = -1; - static ap_event_pod_t *pod; /* The event MPM respects a couple of runtime flags that can aid @@ -358,7 +370,7 @@ static int event_query(int query_code, int *result, apr_status_t *rv) *rv = APR_SUCCESS; switch (query_code) { case AP_MPMQ_MAX_DAEMON_USED: - *result = max_daemons_limit; + *result = retained->max_daemons_limit; break; case AP_MPMQ_IS_THREADED: *result = AP_MPMQ_STATIC; @@ -403,7 +415,7 @@ static int event_query(int query_code, int *result, apr_status_t *rv) *result = mpm_state; break; case AP_MPMQ_GENERATION: - *result = my_generation; + *result = retained->my_generation; break; default: *rv = APR_ENOTIMPL; @@ -448,7 +460,6 @@ static int child_fatal; /* volatile because they're updated from a signal handler */ static int volatile shutdown_pending; static int volatile restart_pending; -static int volatile is_graceful; /* * ap_start_shutdown() and ap_start_restart(), below, are a first stab at @@ -480,7 +491,7 @@ static void ap_start_shutdown(int graceful) return; } shutdown_pending = 1; - is_graceful = graceful; + retained->is_graceful = graceful; } /* do a graceful restart if graceful == 1 */ @@ -492,7 +503,7 @@ static void ap_start_restart(int graceful) return; } restart_pending = 1; - is_graceful = graceful; + retained->is_graceful = graceful; } static void sig_term(int sig) @@ -1332,7 +1343,7 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy) ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid; ap_scoreboard_image->servers[process_slot][thread_slot].tid = apr_os_thread_current(); - ap_scoreboard_image->servers[process_slot][thread_slot].generation = my_generation; + ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->my_generation; ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL); @@ -1828,8 +1839,8 @@ static int make_child(server_rec * s, int slot) { int pid; - if (slot + 1 > max_daemons_limit) { - max_daemons_limit = slot + 1; + if (slot + 1 > retained->max_daemons_limit) { + retained->max_daemons_limit = slot + 1; } if (one_process) { @@ -1913,19 +1924,6 @@ static void startup_children(int number_to_start) } } - -/* - * idle_spawn_rate is the number of children that will be spawned on the - * next maintenance cycle if there aren't enough idle servers. It is - * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by - * without the need to spawn. - */ -static int idle_spawn_rate = 1; -#ifndef MAX_SPAWN_RATE -#define MAX_SPAWN_RATE (32) -#endif -static int hold_off_on_exponential_spawning; - static void perform_idle_server_maintenance(void) { int i, j; @@ -1954,8 +1952,8 @@ static void perform_idle_server_maintenance(void) int any_dead_threads = 0; int all_dead_threads = 1; - if (i >= max_daemons_limit - && totally_free_length == idle_spawn_rate) + if (i >= retained->max_daemons_limit + && totally_free_length == retained->idle_spawn_rate) /* short cut if all active processes have been examined and * enough empty scoreboard slots have been found */ @@ -1982,7 +1980,7 @@ static void perform_idle_server_maintenance(void) if (ps->pid != 0) { /* XXX just set all_dead_threads in outer for loop if no pid? not much else matters */ if (status <= SERVER_READY && - !ps->quiescing && ps->generation == my_generation) { + !ps->quiescing && ps->generation == retained->my_generation) { ++idle_thread_count; } if (status >= SERVER_READY && status < SERVER_GRACEFUL) { @@ -1991,7 +1989,7 @@ static void perform_idle_server_maintenance(void) } } if (any_dead_threads - && totally_free_length < idle_spawn_rate + && totally_free_length < retained->idle_spawn_rate && free_length < MAX_SPAWN_RATE && (!ps->pid /* no process in the slot */ || ps->quiescing)) { /* or at least one is going away */ @@ -2020,12 +2018,12 @@ static void perform_idle_server_maintenance(void) } } - if (sick_child_detected) { + if (retained->sick_child_detected) { if (active_thread_count > 0) { /* some child processes appear to be working. don't kill the * whole server. */ - sick_child_detected = 0; + retained->sick_child_detected = 0; } else { /* looks like a basket case. give up. @@ -2041,26 +2039,25 @@ static void perform_idle_server_maintenance(void) } } - max_daemons_limit = last_non_dead + 1; + retained->max_daemons_limit = last_non_dead + 1; if (idle_thread_count > max_spare_threads) { /* Kill off one child */ ap_event_pod_signal(pod, TRUE); - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else if (idle_thread_count < min_spare_threads) { /* terminate the free list */ if (free_length == 0) { /* scoreboard is full, can't fork */ if (active_thread_count >= ap_daemons_limit * threads_per_child) { - static int reported = 0; - if (!reported) { + if (!retained->maxclients_reported) { /* only report this condition once */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "server reached MaxClients setting, consider" " raising the MaxClients setting"); - reported = 1; + retained->maxclients_reported = 1; } } else { @@ -2068,13 +2065,13 @@ static void perform_idle_server_maintenance(void) ap_server_conf, "scoreboard is full, not at MaxClients"); } - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else { - if (free_length > idle_spawn_rate) { - free_length = idle_spawn_rate; + if (free_length > retained->idle_spawn_rate) { + free_length = retained->idle_spawn_rate; } - if (idle_spawn_rate >= 8) { + if (retained->idle_spawn_rate >= 8) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, "server seems busy, (you may need " @@ -2090,16 +2087,16 @@ static void perform_idle_server_maintenance(void) /* the next time around we want to spawn twice as many if this * wasn't good enough, but not if we've just done a graceful */ - if (hold_off_on_exponential_spawning) { - --hold_off_on_exponential_spawning; + if (retained->hold_off_on_exponential_spawning) { + --retained->hold_off_on_exponential_spawning; } - else if (idle_spawn_rate < MAX_SPAWN_RATE) { - idle_spawn_rate *= 2; + else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) { + retained->idle_spawn_rate *= 2; } } } else { - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } } @@ -2125,7 +2122,7 @@ static void server_main_loop(int remaining_children_to_start) /* tell perform_idle_server_maintenance to check into this * on the next timer pop */ - sick_child_detected = 1; + retained->sick_child_detected = 1; } /* non-fatal death... note that it's gone in the scoreboard. */ child_slot = ap_find_child_by_pid(&pid); @@ -2139,7 +2136,7 @@ static void server_main_loop(int remaining_children_to_start) ap_scoreboard_image->parent[child_slot].quiescing = 0; if (processed_status == APEXIT_CHILDSICK) { /* resource shortage, minimize the fork rate */ - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else if (remaining_children_to_start && child_slot < ap_daemons_limit) { @@ -2159,7 +2156,7 @@ static void server_main_loop(int remaining_children_to_start) /* handled */ #endif } - else if (is_graceful) { + else if (retained->is_graceful) { /* Great, we've probably just lost a slot in the * scoreboard. Somehow we don't know about this child. */ @@ -2199,7 +2196,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) ap_log_pid(pconf, ap_pid_fname); - if (!is_graceful) { + if (!retained->is_graceful) { if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { mpm_state = AP_MPMQ_STOPPING; return DONE; @@ -2207,7 +2204,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) /* fix the generation number in the global score; we just got a new, * cleared scoreboard */ - ap_scoreboard_image->global->running_generation = my_generation; + ap_scoreboard_image->global->running_generation = retained->my_generation; } set_signals(); @@ -2228,14 +2225,14 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) if (remaining_children_to_start > ap_daemons_limit) { remaining_children_to_start = ap_daemons_limit; } - if (!is_graceful) { + if (!retained->is_graceful) { startup_children(remaining_children_to_start); remaining_children_to_start = 0; } else { /* give the system some time to recover before kicking into * exponential mode */ - hold_off_on_exponential_spawning = 10; + retained->hold_off_on_exponential_spawning = 10; } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, @@ -2251,7 +2248,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) server_main_loop(remaining_children_to_start); mpm_state = AP_MPMQ_STOPPING; - if (shutdown_pending && !is_graceful) { + if (shutdown_pending && !retained->is_graceful) { /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... */ @@ -2347,10 +2344,10 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) /* XXX: we really need to make sure this new generation number isn't in * use by any of the children. */ - ++my_generation; - ap_scoreboard_image->global->running_generation = my_generation; + ++retained->my_generation; + ap_scoreboard_image->global->running_generation = retained->my_generation; - if (is_graceful) { + if (retained->is_graceful) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, AP_SIG_GRACEFUL_STRING " received. Doing graceful restart"); @@ -2439,6 +2436,8 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog, retained = ap_retained_data_get(userdata_key); if (!retained) { retained = ap_retained_data_create(userdata_key, sizeof(*retained)); + retained->max_daemons_limit = -1; + retained->idle_spawn_rate = 1; } ++retained->module_loads; if (retained->module_loads == 2) { @@ -2462,9 +2461,10 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog, return HTTP_INTERNAL_SERVER_ERROR; } } - parent_pid = ap_my_pid = getpid(); } + parent_pid = ap_my_pid = getpid(); + ap_listen_pre_config(); ap_daemons_to_start = DEFAULT_START_DAEMON; min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD; diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index bf7a5faaa7c..7175287c840 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -102,18 +102,31 @@ static ap_pod_t *pod; typedef struct prefork_retained_data { int first_server_limit; int module_loads; + ap_generation_t my_generation; + int volatile is_graceful; /* set from signal handler */ + int maxclients_reported; + /* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We + * use this value to optimize routines that have to scan the entire scoreboard. + */ + int max_daemons_limit; + /* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ + int idle_spawn_rate; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif + int hold_off_on_exponential_spawning; } prefork_retained_data; static prefork_retained_data *retained; #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) -/* - * The max child slot ever assigned, preserved across restarts. Necessary - * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We - * use this value to optimize routines that have to scan the entire scoreboard. - */ -static int max_daemons_limit = -1; - /* one_process --- debugging mode variable; can be set from the command line * with the -X flag. If set, this gets you the child_main loop running * in the process which originally started up (no detach, no make_child), @@ -133,7 +146,6 @@ static apr_pool_t *pchild; /* Pool for httpd child stuff */ static pid_t ap_my_pid; /* it seems silly to call getpid all the time */ static pid_t parent_pid; static int my_child_num; -static ap_generation_t my_generation=0; #ifdef GPROF /* @@ -197,7 +209,7 @@ static void accept_mutex_on(void) if (rv != APR_SUCCESS) { const char *msg = "couldn't grab the accept mutex"; - if (my_generation != + if (retained->my_generation != ap_scoreboard_image->global->running_generation) { ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, "%s", msg); clean_child_exit(0); @@ -215,7 +227,7 @@ static void accept_mutex_off(void) if (rv != APR_SUCCESS) { const char *msg = "couldn't release the accept mutex"; - if (my_generation != + if (retained->my_generation != ap_scoreboard_image->global->running_generation) { ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, "%s", msg); /* don't exit here... we have a connection to @@ -285,7 +297,7 @@ static int prefork_query(int query_code, int *result, apr_status_t *rv) *result = mpm_state; break; case AP_MPMQ_GENERATION: - *result = my_generation; + *result = retained->my_generation; break; default: *rv = APR_ENOTIMPL; @@ -317,7 +329,6 @@ static void just_die(int sig) /* volatile because they're updated from a signal handler */ static int volatile shutdown_pending; static int volatile restart_pending; -static int volatile is_graceful; static int volatile die_now = 0; static void stop_listening(int sig) @@ -340,7 +351,7 @@ static void sig_term(int sig) } mpm_state = AP_MPMQ_STOPPING; shutdown_pending = 1; - is_graceful = (sig == AP_SIG_GRACEFUL_STOP); + retained->is_graceful = (sig == AP_SIG_GRACEFUL_STOP); } /* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL @@ -354,7 +365,7 @@ static void restart(int sig) } mpm_state = AP_MPMQ_STOPPING; restart_pending = 1; - is_graceful = (sig == AP_SIG_GRACEFUL); + retained->is_graceful = (sig == AP_SIG_GRACEFUL); } static void set_signals(void) @@ -674,7 +685,7 @@ static void child_main(int child_num_arg) if (ap_mpm_pod_check(pod) == APR_SUCCESS) { /* selected as idle? */ die_now = 1; } - else if (my_generation != + else if (retained->my_generation != ap_scoreboard_image->global->running_generation) { /* restart? */ /* yeah, this could be non-graceful restart, in which case the * parent will kill us soon enough, but why bother checking? @@ -691,8 +702,8 @@ static int make_child(server_rec *s, int slot) { int pid; - if (slot + 1 > max_daemons_limit) { - max_daemons_limit = slot + 1; + if (slot + 1 > retained->max_daemons_limit) { + retained->max_daemons_limit = slot + 1; } if (one_process) { @@ -783,19 +794,6 @@ static void startup_children(int number_to_start) } } - -/* - * idle_spawn_rate is the number of children that will be spawned on the - * next maintenance cycle if there aren't enough idle servers. It is - * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by - * without the need to spawn. - */ -static int idle_spawn_rate = 1; -#ifndef MAX_SPAWN_RATE -#define MAX_SPAWN_RATE (32) -#endif -static int hold_off_on_exponential_spawning; - static void perform_idle_server_maintenance(apr_pool_t *p) { int i; @@ -816,13 +814,13 @@ static void perform_idle_server_maintenance(apr_pool_t *p) for (i = 0; i < ap_daemons_limit; ++i) { int status; - if (i >= max_daemons_limit && free_length == idle_spawn_rate) + if (i >= retained->max_daemons_limit && free_length == retained->idle_spawn_rate) break; ws = &ap_scoreboard_image->servers[i][0]; status = ws->status; if (status == SERVER_DEAD) { /* try to keep children numbers as low as possible */ - if (free_length < idle_spawn_rate) { + if (free_length < retained->idle_spawn_rate) { free_slots[free_length] = i; ++free_length; } @@ -842,36 +840,34 @@ static void perform_idle_server_maintenance(apr_pool_t *p) last_non_dead = i; } } - max_daemons_limit = last_non_dead + 1; + retained->max_daemons_limit = last_non_dead + 1; if (idle_count > ap_daemons_max_free) { /* kill off one child... we use the pod because that'll cause it to * shut down gracefully, in case it happened to pick up a request * while we were counting */ ap_mpm_pod_signal(pod); - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else if (idle_count < ap_daemons_min_free) { /* terminate the free list */ if (free_length == 0) { /* only report this condition once */ - static int reported = 0; - - if (!reported) { + if (!retained->maxclients_reported) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "server reached MaxClients setting, consider" " raising the MaxClients setting"); - reported = 1; + retained->maxclients_reported = 1; } - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else { - if (idle_spawn_rate >= 8) { + if (retained->idle_spawn_rate >= 8) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, "server seems busy, (you may need " "to increase StartServers, or Min/MaxSpareServers), " "spawning %d children, there are %d idle, and " - "%d total children", idle_spawn_rate, + "%d total children", retained->idle_spawn_rate, idle_count, total_non_dead); } for (i = 0; i < free_length; ++i) { @@ -880,16 +876,16 @@ static void perform_idle_server_maintenance(apr_pool_t *p) /* the next time around we want to spawn twice as many if this * wasn't good enough, but not if we've just done a graceful */ - if (hold_off_on_exponential_spawning) { - --hold_off_on_exponential_spawning; + if (retained->hold_off_on_exponential_spawning) { + --retained->hold_off_on_exponential_spawning; } - else if (idle_spawn_rate < MAX_SPAWN_RATE) { - idle_spawn_rate *= 2; + else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) { + retained->idle_spawn_rate *= 2; } } } else { - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } } @@ -913,7 +909,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) return DONE; } - if (!is_graceful) { + if (!retained->is_graceful) { if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { mpm_state = AP_MPMQ_STOPPING; return DONE; @@ -921,7 +917,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* fix the generation number in the global score; we just got a new, * cleared scoreboard */ - ap_scoreboard_image->global->running_generation = my_generation; + ap_scoreboard_image->global->running_generation = retained->my_generation; } set_signals(); @@ -947,7 +943,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if (remaining_children_to_start > ap_daemons_limit) { remaining_children_to_start = ap_daemons_limit; } - if (!is_graceful) { + if (!retained->is_graceful) { startup_children(remaining_children_to_start); remaining_children_to_start = 0; } @@ -955,7 +951,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* give the system some time to recover before kicking into * exponential mode */ - hold_off_on_exponential_spawning = 10; + retained->hold_off_on_exponential_spawning = 10; } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, @@ -1001,7 +997,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc) * cut the fork rate to the minimum */ - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else if (remaining_children_to_start && child_slot < ap_daemons_limit) { @@ -1017,7 +1013,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* handled */ #endif } - else if (is_graceful) { + else if (retained->is_graceful) { /* Great, we've probably just lost a slot in the * scoreboard. Somehow we don't know about this * child. @@ -1053,7 +1049,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) mpm_state = AP_MPMQ_STOPPING; - if (shutdown_pending && !is_graceful) { + if (shutdown_pending && !retained->is_graceful) { /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... */ @@ -1090,7 +1086,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) ap_close_listeners(); /* kill off the idle ones */ - ap_mpm_pod_killpg(pod, max_daemons_limit); + ap_mpm_pod_killpg(pod, retained->max_daemons_limit); /* Send SIGUSR1 to the active children */ active_children = 0; @@ -1165,15 +1161,15 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* XXX: we really need to make sure this new generation number isn't in * use by any of the children. */ - ++my_generation; - ap_scoreboard_image->global->running_generation = my_generation; + ++retained->my_generation; + ap_scoreboard_image->global->running_generation = retained->my_generation; - if (is_graceful) { + if (retained->is_graceful) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "Graceful restart requested, doing restart"); /* kill off the idle ones */ - ap_mpm_pod_killpg(pod, max_daemons_limit); + ap_mpm_pod_killpg(pod, retained->max_daemons_limit); /* This is mostly for debugging... so that we know what is still * gracefully dealing with existing request. This will break @@ -1267,6 +1263,8 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp retained = ap_retained_data_get(userdata_key); if (!retained) { retained = ap_retained_data_create(userdata_key, sizeof(*retained)); + retained->max_daemons_limit = -1; + retained->idle_spawn_rate = 1; } ++retained->module_loads; if (retained->module_loads == 2) { @@ -1279,10 +1277,10 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp return HTTP_INTERNAL_SERVER_ERROR; } } - - parent_pid = ap_my_pid = getpid(); } + parent_pid = ap_my_pid = getpid(); + ap_listen_pre_config(); ap_daemons_to_start = DEFAULT_START_DAEMON; ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON; diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 58067da83e1..b5f6b860eee 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -132,8 +132,6 @@ static int resource_shortage = 0; static fd_queue_t *worker_queue; static fd_queue_info_t *worker_queue_info; static int mpm_state = AP_MPMQ_STARTING; -static int sick_child_detected; -static ap_generation_t my_generation = 0; /* data retained by worker across load/unload of the module * allocated on first call to pre-config hook; located on @@ -143,6 +141,29 @@ typedef struct worker_retained_data { int first_server_limit; int first_thread_limit; int module_loads; + int sick_child_detected; + ap_generation_t my_generation; + int volatile is_graceful; /* set from signal handler */ + int maxclients_reported; + int near_maxclients_reported; + /* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We + * use this value to optimize routines that have to scan the entire + * scoreboard. + */ + int max_daemons_limit; + /* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ + int idle_spawn_rate; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif + int hold_off_on_exponential_spawning; } worker_retained_data; static worker_retained_data *retained; @@ -167,14 +188,6 @@ typedef struct { #define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t) -/* - * The max child slot ever assigned, preserved across restarts. Necessary - * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We - * use this value to optimize routines that have to scan the entire - * scoreboard. - */ -static int max_daemons_limit = -1; - static ap_worker_pod_t *pod; /* The worker MPM respects a couple of runtime flags that can aid @@ -311,7 +324,7 @@ static int worker_query(int query_code, int *result, apr_status_t *rv) *rv = APR_SUCCESS; switch (query_code) { case AP_MPMQ_MAX_DAEMON_USED: - *result = max_daemons_limit; + *result = retained->max_daemons_limit; break; case AP_MPMQ_IS_THREADED: *result = AP_MPMQ_STATIC; @@ -350,7 +363,7 @@ static int worker_query(int query_code, int *result, apr_status_t *rv) *result = mpm_state; break; case AP_MPMQ_GENERATION: - *result = my_generation; + *result = retained->my_generation; break; default: *rv = APR_ENOTIMPL; @@ -395,7 +408,6 @@ static int child_fatal; /* volatile because they're updated from a signal handler */ static int volatile shutdown_pending; static int volatile restart_pending; -static int volatile is_graceful; /* * ap_start_shutdown() and ap_start_restart(), below, are a first stab at @@ -427,7 +439,7 @@ static void ap_start_shutdown(int graceful) return; } shutdown_pending = 1; - is_graceful = graceful; + retained->is_graceful = graceful; } /* do a graceful restart if graceful == 1 */ @@ -439,7 +451,7 @@ static void ap_start_restart(int graceful) return; } restart_pending = 1; - is_graceful = graceful; + retained->is_graceful = graceful; } static void sig_term(int sig) @@ -858,7 +870,7 @@ static void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy) ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid; ap_scoreboard_image->servers[process_slot][thread_slot].tid = apr_os_thread_current(); - ap_scoreboard_image->servers[process_slot][thread_slot].generation = my_generation; + ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->my_generation; ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL); #ifdef HAVE_PTHREAD_KILL @@ -1324,8 +1336,8 @@ static int make_child(server_rec *s, int slot) { int pid; - if (slot + 1 > max_daemons_limit) { - max_daemons_limit = slot + 1; + if (slot + 1 > retained->max_daemons_limit) { + retained->max_daemons_limit = slot + 1; } if (one_process) { @@ -1408,19 +1420,6 @@ static void startup_children(int number_to_start) } } - -/* - * idle_spawn_rate is the number of children that will be spawned on the - * next maintenance cycle if there aren't enough idle servers. It is - * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by - * without the need to spawn. - */ -static int idle_spawn_rate = 1; -#ifndef MAX_SPAWN_RATE -#define MAX_SPAWN_RATE (32) -#endif -static int hold_off_on_exponential_spawning; - static void perform_idle_server_maintenance(void) { int i, j; @@ -1449,7 +1448,7 @@ static void perform_idle_server_maintenance(void) int any_dead_threads = 0; int all_dead_threads = 1; - if (i >= max_daemons_limit && totally_free_length == idle_spawn_rate) + if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate) /* short cut if all active processes have been examined and * enough empty scoreboard slots have been found */ @@ -1477,7 +1476,7 @@ static void perform_idle_server_maintenance(void) loop if no pid? not much else matters */ if (status <= SERVER_READY && !ps->quiescing && - ps->generation == my_generation) { + ps->generation == retained->my_generation) { ++idle_thread_count; } if (status >= SERVER_READY && status < SERVER_GRACEFUL) { @@ -1485,7 +1484,7 @@ static void perform_idle_server_maintenance(void) } } } - if (any_dead_threads && totally_free_length < idle_spawn_rate + if (any_dead_threads && totally_free_length < retained->idle_spawn_rate && free_length < MAX_SPAWN_RATE && (!ps->pid /* no process in the slot */ || ps->quiescing)) { /* or at least one is going away */ @@ -1514,12 +1513,12 @@ static void perform_idle_server_maintenance(void) } } - if (sick_child_detected) { + if (retained->sick_child_detected) { if (active_thread_count > 0) { /* some child processes appear to be working. don't kill the * whole server. */ - sick_child_detected = 0; + retained->sick_child_detected = 0; } else { /* looks like a basket case. give up. @@ -1535,12 +1534,12 @@ static void perform_idle_server_maintenance(void) } } - max_daemons_limit = last_non_dead + 1; + retained->max_daemons_limit = last_non_dead + 1; if (idle_thread_count > max_spare_threads) { /* Kill off one child */ ap_worker_pod_signal(pod, TRUE); - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else if (idle_thread_count < min_spare_threads) { /* terminate the free list */ @@ -1550,23 +1549,21 @@ static void perform_idle_server_maintenance(void) /* no threads are "inactive" - starting, stopping, etc. */ /* have we reached MaxClients, or just getting close? */ if (0 == idle_thread_count) { - static int reported = 0; - if (!reported) { + if (!retained->maxclients_reported) { /* only report this condition once */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "server reached MaxClients setting, consider" " raising the MaxClients setting"); - reported = 1; + retained->maxclients_reported = 1; } } else { - static int reported = 0; - if (!reported) { + if (!retained->near_maxclients_reported) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "server is within MinSpareThreads of MaxClients, " "consider raising the MaxClients setting"); - reported = 1; + retained->near_maxclients_reported = 1; } } } @@ -1575,13 +1572,13 @@ static void perform_idle_server_maintenance(void) ap_server_conf, "scoreboard is full, not at MaxClients"); } - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else { - if (free_length > idle_spawn_rate) { - free_length = idle_spawn_rate; + if (free_length > retained->idle_spawn_rate) { + free_length = retained->idle_spawn_rate; } - if (idle_spawn_rate >= 8) { + if (retained->idle_spawn_rate >= 8) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, "server seems busy, (you may need " @@ -1597,16 +1594,16 @@ static void perform_idle_server_maintenance(void) /* the next time around we want to spawn twice as many if this * wasn't good enough, but not if we've just done a graceful */ - if (hold_off_on_exponential_spawning) { - --hold_off_on_exponential_spawning; + if (retained->hold_off_on_exponential_spawning) { + --retained->hold_off_on_exponential_spawning; } - else if (idle_spawn_rate < MAX_SPAWN_RATE) { - idle_spawn_rate *= 2; + else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) { + retained->idle_spawn_rate *= 2; } } } else { - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } } @@ -1632,7 +1629,7 @@ static void server_main_loop(int remaining_children_to_start) /* tell perform_idle_server_maintenance to check into this * on the next timer pop */ - sick_child_detected = 1; + retained->sick_child_detected = 1; } /* non-fatal death... note that it's gone in the scoreboard. */ child_slot = ap_find_child_by_pid(&pid); @@ -1645,7 +1642,7 @@ static void server_main_loop(int remaining_children_to_start) ap_scoreboard_image->parent[child_slot].quiescing = 0; if (processed_status == APEXIT_CHILDSICK) { /* resource shortage, minimize the fork rate */ - idle_spawn_rate = 1; + retained->idle_spawn_rate = 1; } else if (remaining_children_to_start && child_slot < ap_daemons_limit) { @@ -1665,7 +1662,7 @@ static void server_main_loop(int remaining_children_to_start) /* handled */ #endif } - else if (is_graceful) { + else if (retained->is_graceful) { /* Great, we've probably just lost a slot in the * scoreboard. Somehow we don't know about this child. */ @@ -1714,7 +1711,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) return DONE; } - if (!is_graceful) { + if (!retained->is_graceful) { if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { mpm_state = AP_MPMQ_STOPPING; return DONE; @@ -1722,7 +1719,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* fix the generation number in the global score; we just got a new, * cleared scoreboard */ - ap_scoreboard_image->global->running_generation = my_generation; + ap_scoreboard_image->global->running_generation = retained->my_generation; } set_signals(); @@ -1743,14 +1740,14 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if (remaining_children_to_start > ap_daemons_limit) { remaining_children_to_start = ap_daemons_limit; } - if (!is_graceful) { + if (!retained->is_graceful) { startup_children(remaining_children_to_start); remaining_children_to_start = 0; } else { /* give the system some time to recover before kicking into * exponential mode */ - hold_off_on_exponential_spawning = 10; + retained->hold_off_on_exponential_spawning = 10; } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, @@ -1769,7 +1766,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) server_main_loop(remaining_children_to_start); mpm_state = AP_MPMQ_STOPPING; - if (shutdown_pending && !is_graceful) { + if (shutdown_pending && !retained->is_graceful) { /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... */ @@ -1865,10 +1862,10 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* XXX: we really need to make sure this new generation number isn't in * use by any of the children. */ - ++my_generation; - ap_scoreboard_image->global->running_generation = my_generation; + ++retained->my_generation; + ap_scoreboard_image->global->running_generation = retained->my_generation; - if (is_graceful) { + if (retained->is_graceful) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, AP_SIG_GRACEFUL_STRING " received. Doing graceful restart"); /* wake up the children...time to die. But we'll have more soon */ @@ -1957,6 +1954,8 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, retained = ap_retained_data_get(userdata_key); if (!retained) { retained = ap_retained_data_create(userdata_key, sizeof(*retained)); + retained->max_daemons_limit = -1; + retained->idle_spawn_rate = 1; } ++retained->module_loads; if (retained->module_loads == 2) { @@ -1969,9 +1968,10 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, return HTTP_INTERNAL_SERVER_ERROR; } } - parent_pid = ap_my_pid = getpid(); } + parent_pid = ap_my_pid = getpid(); + ap_listen_pre_config(); ap_daemons_to_start = DEFAULT_START_DAEMON; min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD; @@ -1994,11 +1994,10 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, static int worker_check_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { - static int restart_num = 0; int startup = 0; /* the reverse of pre_config, we want this only the first time around */ - if (restart_num++ == 0) { + if (retained->module_loads == 1) { startup = 1; }