]> granicus.if.org Git - apache/blobdiff - server/mpm/worker/worker.c
Add child_status hook for tracking creation/termination of MPM child
[apache] / server / mpm / worker / worker.c
index 3a9efb60bbfd0e2172c7bbed40a74135d29538fa..752679d4589492ae234c06839399436eff46b6bd 100644 (file)
@@ -64,6 +64,7 @@
 #include "scoreboard.h"
 #include "fdqueue.h"
 #include "mpm_default.h"
+#include "util_mutex.h"
 #include "unixd.h"
 
 #include <signal.h>
@@ -131,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 volatile my_generation = 0;
 
 /* data retained by worker across load/unload of the module
  * allocated on first call to pre-config hook; located on
@@ -142,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;
 
@@ -166,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
@@ -305,64 +319,100 @@ static void signal_threads(int mode)
     }
 }
 
-static apr_status_t worker_query(int query_code, int *result)
+static int worker_query(int query_code, int *result, apr_status_t *rv)
 {
-    switch(query_code){
+    *rv = APR_SUCCESS;
+    switch (query_code) {
         case AP_MPMQ_MAX_DAEMON_USED:
-            *result = max_daemons_limit;
-            return APR_SUCCESS;
+            *result = retained->max_daemons_limit;
+            break;
         case AP_MPMQ_IS_THREADED:
             *result = AP_MPMQ_STATIC;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_IS_FORKED:
             *result = AP_MPMQ_DYNAMIC;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_HARD_LIMIT_DAEMONS:
             *result = server_limit;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_HARD_LIMIT_THREADS:
             *result = thread_limit;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MAX_THREADS:
             *result = threads_per_child;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MIN_SPARE_DAEMONS:
             *result = 0;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MIN_SPARE_THREADS:
             *result = min_spare_threads;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MAX_SPARE_DAEMONS:
             *result = 0;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MAX_SPARE_THREADS:
             *result = max_spare_threads;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MAX_REQUESTS_DAEMON:
             *result = ap_max_requests_per_child;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MAX_DAEMONS:
             *result = ap_daemons_limit;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_MPM_STATE:
             *result = mpm_state;
-            return APR_SUCCESS;
+            break;
         case AP_MPMQ_GENERATION:
-            *result = my_generation;
-            return APR_SUCCESS;
+            *result = retained->my_generation;
+            break;
+        default:
+            *rv = APR_ENOTIMPL;
+            break;
     }
-    return APR_ENOTIMPL;
+    return OK;
 }
 
-static pid_t worker_get_child_pid(int childnum)
+static void worker_note_child_killed(int childnum, pid_t pid, ap_generation_t gen)
 {
-    return ap_scoreboard_image->parent[childnum].pid;
+    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 apr_status_t worker_note_child_killed(int childnum)
+static void worker_note_child_started(int slot, pid_t pid)
 {
-    ap_scoreboard_image->parent[childnum].pid = 0;
-    return APR_SUCCESS;
+    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)
@@ -378,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);
 }
 
@@ -390,11 +445,11 @@ static void just_die(int sig)
  * Connection structures and accounting...
  */
 
-/* volatile just in case */
+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;
-static volatile int child_fatal;
 
 /*
  * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
@@ -426,7 +481,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 */
@@ -438,7 +493,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)
@@ -486,7 +541,10 @@ static void set_signals(void)
                      "sigaction(SIGXCPU)");
 #endif
 #ifdef SIGXFSZ
-    sa.sa_handler = SIG_DFL;
+    /* For systems following the LFS standard, ignoring SIGXFSZ allows
+     * a write() beyond the 2GB limit to fail gracefully with E2BIG
+     * rather than terminate the process. */
+    sa.sa_handler = SIG_IGN;
     if (sigaction(SIGXFSZ, &sa, NULL) < 0)
         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
                      "sigaction(SIGXFSZ)");
@@ -515,7 +573,7 @@ static void set_signals(void)
         apr_signal(SIGXCPU, SIG_DFL);
 #endif /* SIGXCPU */
 #ifdef SIGXFSZ
-        apr_signal(SIGXFSZ, SIG_DFL);
+        apr_signal(SIGXFSZ, SIG_IGN);
 #endif /* SIGXFSZ */
     }
 
@@ -564,7 +622,7 @@ static void process_socket(apr_thread_t *thd, apr_pool_t *p, apr_socket_t *sock,
 }
 
 /* requests_this_child has gone to zero or below.  See if the admin coded
-   "MaxRequestsPerChild 0", and keep going in that case.  Doing it this way
+   "MaxConnectionsPerChild 0", and keep going in that case.  Doing it this way
    simplifies the hot path in worker_thread */
 static void check_infinite_requests(void)
 {
@@ -572,17 +630,6 @@ static void check_infinite_requests(void)
         signal_threads(ST_GRACEFUL);
     }
     else {
-        /* wow! if you're executing this code, you may have set a record.
-         * either this child process has served over 2 billion requests, or
-         * you're running a threaded 2.0 on a 16 bit machine.
-         *
-         * I'll buy pizza and beers at Apachecon for the first person to do
-         * the former without cheating (dorking with INT_MAX, or running with
-         * uncommitted performance patches, for example).
-         *
-         * for the latter case, you probably deserve a beer too.   Greg Ames
-         */
-
         requests_this_child = INT_MAX;      /* keep going */
     }
 }
@@ -607,6 +654,29 @@ static void dummy_signal_handler(int sig)
      */
 }
 
+static void accept_mutex_error(const char *func, apr_status_t rv, int process_slot)
+{
+    int level = APLOG_EMERG;
+
+    if (ap_scoreboard_image->parent[process_slot].generation !=
+        ap_scoreboard_image->global->running_generation) {
+        level = APLOG_DEBUG; /* common to get these at restart time */
+    } 
+    else if (requests_this_child == INT_MAX  
+        || ((requests_this_child == ap_max_requests_per_child)
+            && ap_max_requests_per_child)) { 
+        ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
+                     "apr_proc_mutex_%s failed "
+                     "before this child process served any requests.",
+                     func);
+        clean_child_exit(APEXIT_CHILDSICK); 
+    }
+    ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
+                 "apr_proc_mutex_%s failed. Attempting to "
+                 "shutdown process gracefully.", func);
+    signal_threads(ST_GRACEFUL);
+}
+
 static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
 {
     proc_info * ti = dummy;
@@ -622,8 +692,14 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
 
     free(ti);
 
-    /* ### check the status */
-    (void) apr_pollset_create(&pollset, num_listensocks, tpool, 0);
+    rv = apr_pollset_create(&pollset, num_listensocks, tpool, 0);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "Couldn't create pollset in thread;"
+                     " check system or user limits");
+        /* let the parent decide how bad this really is */
+        clean_child_exit(APEXIT_CHILDSICK);
+    }
 
     for (lr = ap_listeners; lr != NULL; lr = lr->next) {
         apr_pollfd_t pfd = { 0 };
@@ -633,8 +709,14 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
         pfd.reqevents = APR_POLLIN;
         pfd.client_data = lr;
 
-        /* ### check the status */
-        (void) apr_pollset_add(pollset, &pfd);
+        rv = apr_pollset_add(pollset, &pfd);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                         "Couldn't create add listener to pollset;"
+                         " check system or user limits");
+            /* let the parent decide how bad this really is */
+            clean_child_exit(APEXIT_CHILDSICK);
+        }
 
         lr->accept_func = ap_unixd_accept;
     }
@@ -678,19 +760,10 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
 
         if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(accept_mutex)))
             != APR_SUCCESS) {
-            int level = APLOG_EMERG;
 
-            if (listener_may_exit) {
-                break;
+            if (!listener_may_exit) {
+                accept_mutex_error("lock", rv, process_slot);
             }
-            if (ap_scoreboard_image->parent[process_slot].generation !=
-                ap_scoreboard_image->global->running_generation) {
-                level = APLOG_DEBUG; /* common to get these at restart time */
-            }
-            ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
-                         "apr_proc_mutex_lock failed. Attempting to shutdown "
-                         "process gracefully.");
-            signal_threads(ST_GRACEFUL);
             break;                    /* skip the lock release */
         }
 
@@ -711,8 +784,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
 
                     /* apr_pollset_poll() will only return errors in catastrophic
                      * circumstances. Let's try exiting gracefully, for now. */
-                    ap_log_error(APLOG_MARK, APLOG_ERR, rv,
-                                 (const server_rec *) ap_server_conf,
+                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
                                  "apr_pollset_poll: (listen)");
                     signal_threads(ST_GRACEFUL);
                 }
@@ -769,19 +841,11 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
             }
             if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex)))
                 != APR_SUCCESS) {
-                int level = APLOG_EMERG;
 
                 if (listener_may_exit) {
                     break;
                 }
-                if (ap_scoreboard_image->parent[process_slot].generation !=
-                    ap_scoreboard_image->global->running_generation) {
-                    level = APLOG_DEBUG; /* common to get these at restart time */
-                }
-                ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
-                             "apr_proc_mutex_unlock failed. Attempting to "
-                             "shutdown process gracefully.");
-                signal_threads(ST_GRACEFUL);
+                accept_mutex_error("unlock", rv, process_slot);
             }
             if (csd != NULL) {
                 rv = ap_queue_push(worker_queue, csd, ptrans);
@@ -851,7 +915,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
@@ -1166,7 +1230,8 @@ static void child_main(int child_num_arg)
     /*stuff to do before we switch id's, so we have permissions.*/
     ap_reopen_scoreboard(pchild, NULL, 0);
 
-    rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname,
+    rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex,
+                                               apr_proc_mutex_lockfile(accept_mutex),
                                                pchild));
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
@@ -1269,7 +1334,7 @@ static void child_main(int child_num_arg)
     else { /* !one_process */
         /* remove SIGTERM from the set of blocked signals...  if one of
          * the other threads in the process needs to take us down
-         * (e.g., for MaxRequestsPerChild) it will send us SIGTERM
+         * (e.g., for MaxConnectionsPerChild) it will send us SIGTERM
          */
         unblock_signal(SIGTERM);
         apr_signal(SIGTERM, dummy_signal_handler);
@@ -1316,14 +1381,15 @@ 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) {
         set_signals();
-        ap_scoreboard_image->parent[slot].pid = getpid();
+        worker_note_child_started(slot, getpid());
         child_main(slot);
+        /* NOTREACHED */
     }
 
     if ((pid = fork()) == -1) {
@@ -1352,35 +1418,26 @@ static int make_child(server_rec *s, int slot)
         int status = bindprocessor(BINDPROCESS, (int)getpid(),
                                PROCESSOR_CLASS_ANY);
         if (status != OK)
-            ap_log_error(APLOG_MARK, APLOG_WARNING, errno,
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, errno,
                          ap_server_conf,
-                         "processor unbind failed %d", status);
+                         "processor unbind failed");
 #endif
         RAISE_SIGSTOP(MAKE_CHILD);
 
         apr_signal(SIGTERM, just_die);
         child_main(slot);
-
-        clean_child_exit(0);
+        /* NOTREACHED */
     }
     /* else */
     if (ap_scoreboard_image->parent[slot].pid != 0) {
         /* 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;
 }
 
@@ -1400,19 +1457,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;
@@ -1441,7 +1485,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
              */
@@ -1469,7 +1513,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) {
@@ -1477,7 +1521,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 */
@@ -1506,12 +1550,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.
@@ -1527,26 +1571,37 @@ 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 */
         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) {
-                    /* 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;
+                /* no threads are "inactive" - starting, stopping, etc. */
+                /* have we reached MaxClients, or just getting close? */
+                if (0 == idle_thread_count) {
+                    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");
+                        retained->maxclients_reported = 1;
+                    }
+                } else {
+                    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");
+                        retained->near_maxclients_reported = 1;
+                    }
                 }
             }
             else {
@@ -1554,13 +1609,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 "
@@ -1576,21 +1631,22 @@ 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;
     }
 }
 
 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;
@@ -1598,7 +1654,7 @@ static void server_main_loop(int remaining_children_to_start)
     int i;
 
     while (!restart_pending && !shutdown_pending) {
-        ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
+        ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
 
         if (pid.pid != -1) {
             processed_status = ap_process_child_status(&pid, exitwhy, status);
@@ -1611,7 +1667,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);
@@ -1620,11 +1676,11 @@ 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 */
-                    idle_spawn_rate = 1;
+                    retained->idle_spawn_rate = 1;
                 }
                 else if (remaining_children_to_start
                     && child_slot < ap_daemons_limit) {
@@ -1635,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,
@@ -1644,7 +1701,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.
                  */
@@ -1686,44 +1743,22 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
     ap_log_pid(pconf, ap_pid_fname);
 
     /* Initialize cross-process accept lock */
-    ap_lock_fname = apr_psprintf(_pconf, "%s.%" APR_PID_T_FMT,
-                                 ap_server_root_relative(_pconf, ap_lock_fname),
-                                 ap_my_pid);
-
-    rv = apr_proc_mutex_create(&accept_mutex, ap_lock_fname,
-                               ap_accept_lock_mech, _pconf);
+    rv = ap_proc_mutex_create(&accept_mutex, NULL, AP_ACCEPT_MUTEX_TYPE, NULL,
+                              s, _pconf, 0);
     if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
-                     "Couldn't create accept lock");
         mpm_state = AP_MPMQ_STOPPING;
-        return 1;
-    }
-
-#if APR_USE_SYSVSEM_SERIALIZE
-    if (ap_accept_lock_mech == APR_LOCK_DEFAULT ||
-        ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
-#else
-    if (ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
-#endif
-        rv = ap_unixd_set_proc_mutex_perms(accept_mutex);
-        if (rv != APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
-                         "Couldn't set permissions on cross-process lock; "
-                         "check User and Group directives");
-            mpm_state = AP_MPMQ_STOPPING;
-            return 1;
-        }
+        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 1;
+            return DONE;
         }
         /* 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();
@@ -1744,14 +1779,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,
@@ -1759,8 +1794,9 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
                 ap_get_server_description());
     ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
                 "Server built: %s", ap_get_server_built());
+    ap_log_command_line(plog, s);
     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
-                "AcceptMutex: %s (default: %s)",
+                "Accept mutex: %s (default: %s)",
                 apr_proc_mutex_name(accept_mutex),
                 apr_proc_mutex_defname());
     restart_pending = shutdown_pending = 0;
@@ -1769,27 +1805,21 @@ 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...
          */
         ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
-        ap_reclaim_child_processes(1);                /* Start with SIGTERM */
+        ap_reclaim_child_processes(1, /* Start with SIGTERM */
+                                   worker_note_child_killed);
 
         if (!child_fatal) {
             /* cleanup pid file on normal shutdown */
-            const char *pidfile = NULL;
-            pidfile = ap_server_root_relative (pconf, ap_pid_fname);
-            if ( pidfile != NULL && unlink(pidfile) == 0)
-                ap_log_error(APLOG_MARK, APLOG_INFO, 0,
-                             ap_server_conf,
-                             "removed PID file %s (pid=%" APR_PID_T_FMT ")",
-                             pidfile, getpid());
-
+            ap_remove_pid(pconf, ap_pid_fname);
             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
                          ap_server_conf, "caught SIGTERM, shutting down");
         }
-        return 1;
+        return DONE;
     } else if (shutdown_pending) {
         /* Time to gracefully shut down:
          * Kill child processes, tell them to call child_exit, etc...
@@ -1801,18 +1831,11 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
         /* Close our listeners, and then ask our children to do same */
         ap_close_listeners();
         ap_worker_pod_killpg(pod, ap_daemons_limit, TRUE);
-        ap_relieve_child_processes();
+        ap_relieve_child_processes(worker_note_child_killed);
 
         if (!child_fatal) {
             /* cleanup pid file on normal shutdown */
-            const char *pidfile = NULL;
-            pidfile = ap_server_root_relative (pconf, ap_pid_fname);
-            if ( pidfile != NULL && unlink(pidfile) == 0)
-                ap_log_error(APLOG_MARK, APLOG_INFO, 0,
-                             ap_server_conf,
-                             "removed PID file %s (pid=%" APR_PID_T_FMT ")",
-                             pidfile, getpid());
-
+            ap_remove_pid(pconf, ap_pid_fname);
             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                          "caught " AP_SIG_GRACEFUL_STOP_STRING
                          ", shutting down gracefully");
@@ -1830,7 +1853,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
             apr_sleep(apr_time_from_sec(1));
 
             /* Relieve any children which have now exited */
-            ap_relieve_child_processes();
+            ap_relieve_child_processes(worker_note_child_killed);
 
             active_children = 0;
             for (index = 0; index < ap_daemons_limit; ++index) {
@@ -1848,9 +1871,9 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
          * really dead.
          */
         ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
-        ap_reclaim_child_processes(1);
+        ap_reclaim_child_processes(1, worker_note_child_killed);
 
-        return 1;
+        return DONE;
     }
 
     /* we've been told to restart */
@@ -1858,17 +1881,17 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
 
     if (one_process) {
         /* not worth thinking about */
-        return 1;
+        return DONE;
     }
 
     /* advance to the next generation */
     /* 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 */
@@ -1887,7 +1910,8 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
          */
         ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
 
-        ap_reclaim_child_processes(1);                /* Start with SIGTERM */
+        ap_reclaim_child_processes(1, /* Start with SIGTERM */
+                                   worker_note_child_killed);
         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                     "SIGHUP received.  Attempting to restart");
     }
@@ -1951,15 +1975,17 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
         foreground = ap_exists_config_define("FOREGROUND");
     }
 
+    ap_mutex_register(pconf, AP_ACCEPT_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
+
     /* sigh, want this only the second time around */
-    retained = ap_get_retained_data(userdata_key);
+    retained = ap_retained_data_get(userdata_key);
     if (!retained) {
-        retained = ap_set_retained_data(userdata_key, sizeof(*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) {
-        is_graceful = 0;
-
         if (!one_process && !foreground) {
             rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
                                            : APR_PROC_DETACH_DAEMONIZE);
@@ -1969,9 +1995,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;
@@ -1981,13 +2008,7 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
     ap_daemons_limit = server_limit;
     threads_per_child = DEFAULT_THREADS_PER_CHILD;
     max_clients = ap_daemons_limit * threads_per_child;
-    ap_pid_fname = DEFAULT_PIDLOG;
-    ap_lock_fname = DEFAULT_LOCKFILE;
-    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
     ap_extended_status = 0;
-    ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-
-    apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
 
     return OK;
 }
@@ -1995,11 +2016,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;
     }
 
@@ -2245,8 +2265,6 @@ static void worker_hooks(apr_pool_t *p)
     ap_hook_check_config(worker_check_config, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm(worker_run, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm_query(worker_query, NULL, NULL, APR_HOOK_MIDDLE);
-    ap_hook_mpm_get_child_pid(worker_get_child_pid, NULL, NULL, APR_HOOK_MIDDLE);
-    ap_hook_mpm_note_child_killed(worker_note_child_killed, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm_get_name(worker_get_name, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
@@ -2352,9 +2370,9 @@ AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND,
 { NULL }
 };
 
-module AP_MODULE_DECLARE_DATA mpm_worker_module = {
+AP_DECLARE_MODULE(mpm_worker) = {
     MPM20_MODULE_STUFF,
-    ap_mpm_rewrite_args,        /* hook to run before apache parses args */
+    NULL,                       /* hook to run before apache parses args */
     NULL,                       /* create per-directory config structure */
     NULL,                       /* merge per-directory config structures */
     NULL,                       /* create per-server config structure */