From: Jeff Trawick Date: Mon, 25 Apr 2011 21:21:22 +0000 (+0000) Subject: Add child_status hook for tracking creation/termination of MPM child X-Git-Tag: 2.3.12~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=462c69e0b533aa86d9a64fbbf8a0f2059520b126;p=thirdparty%2Fapache%2Fhttpd.git Add child_status hook for tracking creation/termination of MPM child processes. Add end_generation hook for notification when the last MPM child of a generation exits. end_generation is implemented completely by core using the child_status hook run by the MPM. simple and mpmt_os2 MPMs don't currently run the child_status hook, so neither hook is invoked with those MPMs. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1096609 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 0a32d4b2d52..eb91935228b 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.3.12 + *) Add child_status hook for tracking creation/termination of MPM child + processes. Add end_generation hook for notification when the last + MPM child of a generation exits. [Jeff Trawick] + *) mod_ldap: Make LDAPSharedCacheSize 0 create a non-shared-memory cache per process as opposed to disabling caching completely. This allows to use the non-shared-memory cache as a workaround for the shared memory cache diff --git a/include/ap_mmn.h b/include/ap_mmn.h index c8817769d80..f5307fe7ed2 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -319,6 +319,7 @@ * 20110329.1 (2.3.12-dev) Add ap_reserve_module_slots()/ap_reserve_module_slots_directive() * change AP_CORE_DECLARE to AP_DECLARE: ap_create_request_config() * change AP_DECLARE to AP_CORE_DECLARE: ap_register_log_hooks() + * 20110329.2 (2.3.12-dev) Add child_status and end_generation hooks. */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ @@ -326,7 +327,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20110329 #endif -#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/ap_mpm.h b/include/ap_mpm.h index ee2c3d273f2..597eafb3218 100644 --- a/include/ap_mpm.h +++ b/include/ap_mpm.h @@ -28,6 +28,7 @@ #include "apr_thread_proc.h" #include "httpd.h" +#include "scoreboard.h" #ifdef __cplusplus extern "C" { @@ -165,7 +166,44 @@ typedef void (ap_mpm_callback_fn_t)(void *baton); AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton); - + +typedef enum mpm_child_status { + MPM_CHILD_STARTED, + MPM_CHILD_EXITED, + MPM_CHILD_LOST_SLOT +} mpm_child_status; + +/** + * Allow a module to remain aware of MPM child process state changes, + * along with the generation and scoreboard slot of the process changing + * state. + * + * With some MPMs (event and worker), an active MPM child process may lose + * its scoreboard slot if the child process is exiting and the scoreboard + * slot is needed by other processes. When this occurs, the hook will be + * called with the MPM_CHILD_LOST_SLOT state. + * + * @param s The main server_rec. + * @param pid The id of the MPM child process. + * @param gen The server generation of that child process. + * @param slot The scoreboard slot number, or -1. It will be -1 when an + * MPM child process exits, and that child had previously lost its + * scoreboard slot. + * @param state One of the mpm_child_status values. Modules should ignore + * unrecognized values. + */ +AP_DECLARE_HOOK(void,child_status,(server_rec *s, pid_t pid, ap_generation_t gen, + int slot, mpm_child_status state)) + +/** + * Allow a module to be notified when the last child process of a generation + * exits. + * + * @param s The main server_rec. + * @param gen The server generation which is now completely finished. + */ +AP_DECLARE_HOOK(void,end_generation,(server_rec *s, ap_generation_t gen)) + /* Defining GPROF when compiling uses the moncontrol() function to * disable gprof profiling in the parent, and enable it only for * request processing in children (or in one_process mode). It's diff --git a/include/mpm_common.h b/include/mpm_common.h index e6583bec140..42bce83fbba 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -39,6 +39,7 @@ #include "ap_config.h" #include "ap_mpm.h" +#include "scoreboard.h" #if APR_HAVE_NETINET_TCP_H #include /* for TCP_NODELAY */ @@ -85,7 +86,8 @@ extern "C" { * ap_relieve_child_processes(). The callback function will be * called for each terminated child process. */ -typedef void ap_reclaim_callback_fn_t(int childnum); +typedef void ap_reclaim_callback_fn_t(int childnum, pid_t pid, + ap_generation_t gen); /** * Make sure all child processes that have been spawned by the parent process @@ -116,21 +118,23 @@ void ap_relieve_child_processes(ap_reclaim_callback_fn_t *mpm_callback); * an MPM child process which has no entry in the scoreboard. * @param pid The process id of an MPM child process which should be * reclaimed when ap_reclaim_child_processes() is called. + * @param gen The generation of this MPM child process. * * @note If an extra MPM child process terminates prior to calling * ap_reclaim_child_processes(), remove it from the list of such processes * by calling ap_unregister_extra_mpm_process(). */ -void ap_register_extra_mpm_process(pid_t pid); +void ap_register_extra_mpm_process(pid_t pid, ap_generation_t gen); /** * Unregister an MPM child process which was previously registered by a * call to ap_register_extra_mpm_process(). * @param pid The process id of an MPM child process which no longer needs to * be reclaimed. + * @param old_gen Set to the server generation of the process, if found. * @return 1 if the process was found and removed, 0 otherwise */ -int ap_unregister_extra_mpm_process(pid_t pid); +int ap_unregister_extra_mpm_process(pid_t pid, ap_generation_t *old_gen); /** * Safely signal an MPM child process, if the process is in the @@ -321,6 +325,10 @@ extern const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy, extern apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *pconf); extern apr_status_t ap_fatal_signal_child_setup(server_rec *s); +/* core's implementation of child_status hook */ +extern void ap_core_child_status(server_rec *s, pid_t pid, ap_generation_t gen, + int slot, mpm_child_status status); + #if AP_ENABLE_EXCEPTION_HOOK extern const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy, const char *arg); diff --git a/server/core.c b/server/core.c index 4d4bb8ce017..83bd0954c56 100644 --- a/server/core.c +++ b/server/core.c @@ -4407,6 +4407,7 @@ static void register_hooks(apr_pool_t *p) APR_OPTIONAL_HOOK(proxy, create_req, core_create_proxy_req, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_pre_mpm(ap_create_scoreboard, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_status(ap_core_child_status, NULL, NULL, APR_HOOK_MIDDLE); /* register the core's insert_filter hook and register core-provided * filters diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 6efd2f73e2a..4efcc903a6a 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -424,9 +424,47 @@ static int event_query(int query_code, int *result, apr_status_t *rv) return OK; } -static void event_note_child_killed(int childnum) +static void event_note_child_killed(int childnum, pid_t pid, ap_generation_t gen) { - ap_scoreboard_image->parent[childnum].pid = 0; + if (childnum != -1) { /* child had a scoreboard slot? */ + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[childnum].pid, + ap_scoreboard_image->parent[childnum].generation, + childnum, MPM_CHILD_EXITED); + ap_scoreboard_image->parent[childnum].pid = 0; + } + else { + ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED); + } +} + +static void event_note_child_started(int slot, pid_t pid) +{ + ap_scoreboard_image->parent[slot].pid = pid; + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + retained->my_generation, slot, MPM_CHILD_STARTED); +} + +static void event_note_child_lost_slot(int slot, pid_t newpid) +{ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, + "pid %" APR_PID_T_FMT " taking over scoreboard slot from " + "%" APR_PID_T_FMT "%s", + newpid, + ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].quiescing ? + " (quiescing)" : ""); + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].generation, + slot, MPM_CHILD_LOST_SLOT); + /* Don't forget about this exiting child process, or we + * won't be able to kill it if it doesn't exit by the + * time the server is shut down. + */ + ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].generation); } static const char *event_get_name(void) @@ -442,6 +480,11 @@ static void clean_child_exit(int code) if (pchild) { apr_pool_destroy(pchild); } + + if (one_process) { + event_note_child_killed(/* slot */ 0, 0, 0); + } + exit(code); } @@ -1847,7 +1890,7 @@ static int make_child(server_rec * s, int slot) if (one_process) { set_signals(); - ap_scoreboard_image->parent[slot].pid = getpid(); + event_note_child_started(slot, getpid()); child_main(slot); /* NOTREACHED */ } @@ -1894,19 +1937,11 @@ static int make_child(server_rec * s, int slot) /* This new child process is squatting on the scoreboard * entry owned by an exiting child process, which cannot * exit until all active requests complete. - * Don't forget about this exiting child process, or we - * won't be able to kill it if it doesn't exit by the - * time the server is shut down. */ - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, - "taking over scoreboard slot from %" APR_PID_T_FMT "%s", - ap_scoreboard_image->parent[slot].pid, - ap_scoreboard_image->parent[slot].quiescing ? - " (quiescing)" : ""); - ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid); + event_note_child_lost_slot(slot, pid); } ap_scoreboard_image->parent[slot].quiescing = 0; - ap_scoreboard_image->parent[slot].pid = pid; + event_note_child_started(slot, pid); return 0; } @@ -2104,6 +2139,7 @@ static void perform_idle_server_maintenance(void) static void server_main_loop(int remaining_children_to_start) { + ap_generation_t old_gen; int child_slot; apr_exit_why_e exitwhy; int status, processed_status; @@ -2134,7 +2170,7 @@ static void server_main_loop(int remaining_children_to_start) SERVER_DEAD, (request_rec *) NULL); - ap_scoreboard_image->parent[child_slot].pid = 0; + event_note_child_killed(child_slot, 0, 0); ap_scoreboard_image->parent[child_slot].quiescing = 0; if (processed_status == APEXIT_CHILDSICK) { /* resource shortage, minimize the fork rate */ @@ -2149,8 +2185,10 @@ static void server_main_loop(int remaining_children_to_start) --remaining_children_to_start; } } - else if (ap_unregister_extra_mpm_process(pid.pid) == 1) { - /* handled */ + else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) { + + event_note_child_killed(-1, /* already out of the scoreboard */ + pid.pid, old_gen); #if APR_HAS_OTHER_CHILD } else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, diff --git a/server/mpm/netware/mpm_netware.c b/server/mpm/netware/mpm_netware.c index 00b2b5ef674..f30fcbf354d 100644 --- a/server/mpm/netware/mpm_netware.c +++ b/server/mpm/netware/mpm_netware.c @@ -880,6 +880,11 @@ static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* Only set slot 0 since that is all NetWare will ever have. */ ap_scoreboard_image->parent[0].pid = getpid(); + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[0].pid, + ap_my_generation, + 0, + MPM_CHILD_STARTED); set_signals(); @@ -917,6 +922,11 @@ static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) } mpm_state = AP_MPMQ_STOPPING; + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[0].pid, + ap_my_generation, + 0, + MPM_CHILD_EXITED); /* Shutdown the listen sockets so that we don't get stuck in a blocking call. shutdown_listeners();*/ diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index 86c8d8c15dd..eecd17739cb 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -189,6 +189,25 @@ static void chdir_for_gprof(void) #define chdir_for_gprof() #endif +static void prefork_note_child_killed(int childnum, pid_t pid, + ap_generation_t gen) +{ + AP_DEBUG_ASSERT(childnum != -1); /* no scoreboard squatting with this MPM */ + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[childnum].pid, + ap_scoreboard_image->parent[childnum].generation, + childnum, MPM_CHILD_EXITED); + ap_scoreboard_image->parent[childnum].pid = 0; +} + +static void prefork_note_child_started(int slot, pid_t pid) +{ + ap_scoreboard_image->parent[slot].pid = pid; + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + retained->my_generation, slot, MPM_CHILD_STARTED); +} + /* a clean exit from a child with proper cleanup */ static void clean_child_exit(int code) __attribute__ ((noreturn)); static void clean_child_exit(int code) @@ -198,6 +217,11 @@ static void clean_child_exit(int code) if (pchild) { apr_pool_destroy(pchild); } + + if (one_process) { + prefork_note_child_killed(/* slot */ 0, 0, 0); + } + ap_mpm_pod_close(pod); chdir_for_gprof(); exit(code); @@ -306,11 +330,6 @@ static int prefork_query(int query_code, int *result, apr_status_t *rv) return OK; } -static void prefork_note_child_killed(int childnum) -{ - ap_scoreboard_image->parent[childnum].pid = 0; -} - static const char *prefork_get_name(void) { return "prefork"; @@ -716,7 +735,7 @@ static int make_child(server_rec *s, int slot) apr_signal(SIGQUIT, SIG_DFL); #endif apr_signal(SIGTERM, sig_term); - ap_scoreboard_image->parent[slot].pid = getpid(); + prefork_note_child_started(slot, getpid()); child_main(slot); /* NOTREACHED */ } @@ -774,7 +793,7 @@ static int make_child(server_rec *s, int slot) child_main(slot); } - ap_scoreboard_image->parent[slot].pid = pid; + prefork_note_child_started(slot, pid); return 0; } @@ -995,6 +1014,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if (child_slot >= 0) { (void) ap_update_child_status_from_indexes(child_slot, 0, SERVER_DEAD, (request_rec *) NULL); + prefork_note_child_killed(child_slot, 0, 0); if (processed_status == APEXIT_CHILDSICK) { /* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc) * cut the fork rate to the minimum diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c index 0d68ea3bc48..e819d863d76 100644 --- a/server/mpm/winnt/mpm_winnt.c +++ b/server/mpm/winnt/mpm_winnt.c @@ -139,6 +139,22 @@ AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF, { NULL } }; +static void winnt_note_child_started(int slot, pid_t pid) +{ + ap_scoreboard_image->parent[slot].pid = pid; + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + my_generation, slot, MPM_CHILD_STARTED); +} + +static void winnt_note_child_killed(int slot) +{ + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].generation, + slot, MPM_CHILD_EXITED); + ap_scoreboard_image->parent[slot].pid = 0; +} /* * Signalling Apache on NT. @@ -767,7 +783,7 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even * child at once. */ ap_scoreboard_image->parent[0].quiescing = 0; - ap_scoreboard_image->parent[0].pid = child_pid; + winnt_note_child_started(/* slot */ 0, child_pid); /* Wait for shutdown or restart events or for child death */ winnt_mpm_state = AP_MPMQ_RUNNING; @@ -843,6 +859,9 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even CloseHandle(event_handles[CHILD_HANDLE]); event_handles[CHILD_HANDLE] = NULL; } + + winnt_note_child_killed(/* slot */ 0); + if (restart_pending) { ++my_generation; ap_scoreboard_image->global->running_generation = my_generation; diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 705451bd222..752679d4589 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -372,9 +372,47 @@ static int worker_query(int query_code, int *result, apr_status_t *rv) return OK; } -static void worker_note_child_killed(int childnum) +static void worker_note_child_killed(int childnum, pid_t pid, ap_generation_t gen) { - ap_scoreboard_image->parent[childnum].pid = 0; + if (childnum != -1) { /* child had a scoreboard slot? */ + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[childnum].pid, + ap_scoreboard_image->parent[childnum].generation, + childnum, MPM_CHILD_EXITED); + ap_scoreboard_image->parent[childnum].pid = 0; + } + else { + ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED); + } +} + +static void worker_note_child_started(int slot, pid_t pid) +{ + ap_scoreboard_image->parent[slot].pid = pid; + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + retained->my_generation, slot, MPM_CHILD_STARTED); +} + +static void worker_note_child_lost_slot(int slot, pid_t newpid) +{ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, + "pid %" APR_PID_T_FMT " taking over scoreboard slot from " + "%" APR_PID_T_FMT "%s", + newpid, + ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].quiescing ? + " (quiescing)" : ""); + ap_run_child_status(ap_server_conf, + ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].generation, + slot, MPM_CHILD_LOST_SLOT); + /* Don't forget about this exiting child process, or we + * won't be able to kill it if it doesn't exit by the + * time the server is shut down. + */ + ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid, + ap_scoreboard_image->parent[slot].generation); } static const char *worker_get_name(void) @@ -390,6 +428,11 @@ static void clean_child_exit(int code) if (pchild) { apr_pool_destroy(pchild); } + + if (one_process) { + worker_note_child_killed(/* slot */ 0, 0, 0); + } + exit(code); } @@ -1344,7 +1387,7 @@ static int make_child(server_rec *s, int slot) if (one_process) { set_signals(); - ap_scoreboard_image->parent[slot].pid = getpid(); + worker_note_child_started(slot, getpid()); child_main(slot); /* NOTREACHED */ } @@ -1390,19 +1433,11 @@ static int make_child(server_rec *s, int slot) /* This new child process is squatting on the scoreboard * entry owned by an exiting child process, which cannot * exit until all active requests complete. - * Don't forget about this exiting child process, or we - * won't be able to kill it if it doesn't exit by the - * time the server is shut down. */ - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, - "taking over scoreboard slot from %" APR_PID_T_FMT "%s", - ap_scoreboard_image->parent[slot].pid, - ap_scoreboard_image->parent[slot].quiescing ? - " (quiescing)" : ""); - ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid); + worker_note_child_lost_slot(slot, pid); } ap_scoreboard_image->parent[slot].quiescing = 0; - ap_scoreboard_image->parent[slot].pid = pid; + worker_note_child_started(slot, pid); return 0; } @@ -1611,6 +1646,7 @@ static void perform_idle_server_maintenance(void) static void server_main_loop(int remaining_children_to_start) { + ap_generation_t old_gen; int child_slot; apr_exit_why_e exitwhy; int status, processed_status; @@ -1640,7 +1676,7 @@ static void server_main_loop(int remaining_children_to_start) ap_update_child_status_from_indexes(child_slot, i, SERVER_DEAD, (request_rec *) NULL); - ap_scoreboard_image->parent[child_slot].pid = 0; + worker_note_child_killed(child_slot, 0, 0); ap_scoreboard_image->parent[child_slot].quiescing = 0; if (processed_status == APEXIT_CHILDSICK) { /* resource shortage, minimize the fork rate */ @@ -1655,8 +1691,9 @@ static void server_main_loop(int remaining_children_to_start) --remaining_children_to_start; } } - else if (ap_unregister_extra_mpm_process(pid.pid) == 1) { - /* handled */ + else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) { + worker_note_child_killed(-1, /* already out of the scoreboard */ + pid.pid, old_gen); #if APR_HAS_OTHER_CHILD } else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, diff --git a/server/mpm_common.c b/server/mpm_common.c index 852ef076ead..02234b792d8 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -67,6 +67,8 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(mpm_query) APR_HOOK_LINK(mpm_register_timed_callback) APR_HOOK_LINK(mpm_get_name) + APR_HOOK_LINK(end_generation) + APR_HOOK_LINK(child_status) ) AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception, (ap_exception_info_t *ei), (ei), OK, DECLINED) @@ -78,6 +80,8 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(mpm_query) APR_HOOK_LINK(mpm_register_timed_callback) APR_HOOK_LINK(mpm_get_name) + APR_HOOK_LINK(end_generation) + APR_HOOK_LINK(child_status) ) #endif AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor, @@ -97,6 +101,22 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback, AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name, (void), (), NULL) +AP_IMPLEMENT_HOOK_VOID(end_generation, + (server_rec *s, ap_generation_t gen), + (s, gen)) +AP_IMPLEMENT_HOOK_VOID(child_status, + (server_rec *s, pid_t pid, ap_generation_t gen, int slot, mpm_child_status status), + (s,pid,gen,slot,status)) + +typedef struct mpm_gen_info_t { + APR_RING_ENTRY(mpm_gen_info_t) link; + int gen; /* which gen? */ + int active; /* number of active processes */ +} mpm_gen_info_t; + +APR_RING_HEAD(mpm_gen_info_head_t, mpm_gen_info_t); +static struct mpm_gen_info_head_t geninfo, unused_geninfo; +static int gen_head_init; /* yuck */ /* variables representing config directives implemented here */ const char *ap_pid_fname; @@ -368,6 +388,75 @@ AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) return rv; } +/* core's child-status hook + * tracks number of remaining children per generation and + * runs the end-generation hook when a generation finishes + */ +void ap_core_child_status(server_rec *s, pid_t pid, + ap_generation_t gen, int slot, + mpm_child_status status) +{ + mpm_gen_info_t *cur; + const char *status_msg = "unknown status"; + + if (!gen_head_init) { /* where to run this? */ + gen_head_init = 1; + APR_RING_INIT(&geninfo, mpm_gen_info_t, link); + APR_RING_INIT(&unused_geninfo, mpm_gen_info_t, link); + } + + cur = APR_RING_FIRST(&geninfo); + while (cur != APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link) && + cur->gen != gen) { + cur = APR_RING_NEXT(cur, link); + } + + switch(status) { + case MPM_CHILD_STARTED: + status_msg = "started"; + if (cur == APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link)) { + /* first child for this generation */ + if (!APR_RING_EMPTY(&unused_geninfo, mpm_gen_info_t, link)) { + cur = APR_RING_FIRST(&unused_geninfo); + APR_RING_REMOVE(cur, link); + } + else { + cur = apr_pcalloc(s->process->pool, sizeof *cur); + } + cur->gen = gen; + APR_RING_ELEM_INIT(cur, link); + APR_RING_INSERT_HEAD(&geninfo, cur, mpm_gen_info_t, link); + } + ++cur->active; + break; + case MPM_CHILD_EXITED: + status_msg = "exited"; + if (cur == APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "no record of generation %d of exiting child %" APR_PID_T_FMT, + gen, pid); + } + else { + --cur->active; + if (!cur->active) { + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, + "end of generation %d", gen); + ap_run_end_generation(ap_server_conf, gen); + APR_RING_REMOVE(cur, link); + APR_RING_INSERT_HEAD(&unused_geninfo, cur, mpm_gen_info_t, link); + } + } + break; + case MPM_CHILD_LOST_SLOT: + status_msg = "lost slot"; + /* we don't track by slot, so it doesn't matter */ + break; + } + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s, + "mpm child %" APR_PID_T_FMT " (gen %d/slot %d) %s", + pid, gen, slot, status_msg); +} + AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton) { return ap_run_mpm_register_timed_callback(t, cbfn, baton); diff --git a/server/mpm_unix.c b/server/mpm_unix.c index 467070e598c..eec51719587 100644 --- a/server/mpm_unix.c +++ b/server/mpm_unix.c @@ -65,20 +65,22 @@ typedef enum {DO_NOTHING, SEND_SIGTERM, SEND_SIGKILL, GIVEUP} action_t; typedef struct extra_process_t { struct extra_process_t *next; pid_t pid; + ap_generation_t gen; } extra_process_t; static extra_process_t *extras; -void ap_register_extra_mpm_process(pid_t pid) +void ap_register_extra_mpm_process(pid_t pid, ap_generation_t gen) { extra_process_t *p = (extra_process_t *)malloc(sizeof(extra_process_t)); p->next = extras; p->pid = pid; + p->gen = gen; extras = p; } -int ap_unregister_extra_mpm_process(pid_t pid) +int ap_unregister_extra_mpm_process(pid_t pid, ap_generation_t *gen) { extra_process_t *cur = extras; extra_process_t *prev = NULL; @@ -95,6 +97,7 @@ int ap_unregister_extra_mpm_process(pid_t pid) else { extras = cur->next; } + *gen = cur->gen; free(cur); return 1; /* found */ } @@ -231,7 +234,7 @@ void ap_reclaim_child_processes(int terminate, } if (reclaim_one_pid(pid, action_table[cur_action].action)) { - mpm_callback(i); + mpm_callback(i, 0, 0); } else { ++not_dead_yet; @@ -240,10 +243,12 @@ void ap_reclaim_child_processes(int terminate, cur_extra = extras; while (cur_extra) { + ap_generation_t old_gen; extra_process_t *next = cur_extra->next; if (reclaim_one_pid(cur_extra->pid, action_table[cur_action].action)) { - AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid)); + AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen)); + mpm_callback(-1, cur_extra->pid, old_gen); } else { ++not_dead_yet; @@ -276,16 +281,18 @@ void ap_relieve_child_processes(ap_reclaim_callback_fn_t *mpm_callback) } if (reclaim_one_pid(pid, DO_NOTHING)) { - mpm_callback(i); + mpm_callback(i, 0, 0); } } cur_extra = extras; while (cur_extra) { + ap_generation_t old_gen; extra_process_t *next = cur_extra->next; if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) { - AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid)); + AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen)); + mpm_callback(-1, cur_extra->pid, old_gen); } cur_extra = next; }