]> granicus.if.org Git - apache/commitdiff
Add child_status hook for tracking creation/termination of MPM child
authorJeff Trawick <trawick@apache.org>
Mon, 25 Apr 2011 21:21:22 +0000 (21:21 +0000)
committerJeff Trawick <trawick@apache.org>
Mon, 25 Apr 2011 21:21:22 +0000 (21:21 +0000)
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

12 files changed:
CHANGES
include/ap_mmn.h
include/ap_mpm.h
include/mpm_common.h
server/core.c
server/mpm/event/event.c
server/mpm/netware/mpm_netware.c
server/mpm/prefork/prefork.c
server/mpm/winnt/mpm_winnt.c
server/mpm/worker/worker.c
server/mpm_common.c
server/mpm_unix.c

diff --git a/CHANGES b/CHANGES
index 0a32d4b2d527f7f91affdc2c36102519387d4906..eb91935228b3dde298bd3ed70a7fe44dff3734e4 100644 (file)
--- 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
index c8817769d80c28679fa8f4868f78fbd124e2f7ec..f5307fe7ed2c5d53318ed2e290705b0581c0dc3b 100644 (file)
  * 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" */
 #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
index ee2c3d273f289f109f743cdcddc312b92e76ec0b..597eafb321824d73a2c5e22d73ce4aea3d820056 100644 (file)
@@ -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
index e6583bec1405ab3f71da80e796e69ef1a13110a1..42bce83fbba7feff8fba29221d2c3b2a60546d43 100644 (file)
@@ -39,6 +39,7 @@
 
 #include "ap_config.h"
 #include "ap_mpm.h"
+#include "scoreboard.h"
 
 #if APR_HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>    /* 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);
index 4d4bb8ce017b2930e515aa63953995c8b63acf39..83bd0954c56fde2a24fea8366c7cf2c72cd9c868 100644 (file)
@@ -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
index 6efd2f73e2a33123b5b65e524b3ce4c97e6df7d7..4efcc903a6a176567aa15341771e61e211a845a6 100644 (file)
@@ -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,
index 00b2b5ef674400059b6995a9292428a82d878fa0..f30fcbf354d2cd7cb81535f868ead323685275bf 100644 (file)
@@ -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();*/
index 86c8d8c15dd40bcc3beefee73593df420ad1b6fa..eecd17739cb5d288f6a5ce4ef63c7aec0100d6a2 100644 (file)
@@ -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
index 0d68ea3bc48bb61d4c082d6943f9f0e95dce5398..e819d863d76b34614f576851b7310046fd92cd14 100644 (file)
@@ -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;
index 705451bd222b619446eaea19679ebd37342c68f4..752679d4589492ae234c06839399436eff46b6bd 100644 (file)
@@ -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,
index 852ef076ead1fba7f7eb3388870c68fd8c8ce328..02234b792d804835f297c0441df896af1caa126b 100644 (file)
@@ -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);
index 467070e598c2af76418382034b3dd28f769a2560..eec51719587d6fe69c4dd37c411b934d503a591f 100644 (file)
@@ -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;
     }