310e38
--- a/server/mpm/event/event.c	2022/05/24 08:59:30	1901198
310e38
+++ b/server/mpm/event/event.c	2022/05/24 09:00:19	1901199
310e38
@@ -379,7 +379,7 @@
310e38
      * We use this value to optimize routines that have to scan the entire
310e38
      * scoreboard.
310e38
      */
310e38
-    int max_daemons_limit;
310e38
+    int max_daemon_used;
310e38
 
310e38
     /*
310e38
      * All running workers, active and shutting down, including those that
310e38
@@ -645,7 +645,7 @@
310e38
     *rv = APR_SUCCESS;
310e38
     switch (query_code) {
310e38
     case AP_MPMQ_MAX_DAEMON_USED:
310e38
-        *result = retained->max_daemons_limit;
310e38
+        *result = retained->max_daemon_used;
310e38
         break;
310e38
     case AP_MPMQ_IS_THREADED:
310e38
         *result = AP_MPMQ_STATIC;
310e38
@@ -696,14 +696,32 @@
310e38
     return OK;
310e38
 }
310e38
 
310e38
-static void event_note_child_killed(int childnum, pid_t pid, ap_generation_t gen)
310e38
+static void event_note_child_stopped(int slot, pid_t pid, ap_generation_t gen)
310e38
 {
310e38
-    if (childnum != -1) { /* child had a scoreboard slot? */
310e38
-        ap_run_child_status(ap_server_conf,
310e38
-                            ap_scoreboard_image->parent[childnum].pid,
310e38
-                            ap_scoreboard_image->parent[childnum].generation,
310e38
-                            childnum, MPM_CHILD_EXITED);
310e38
-        ap_scoreboard_image->parent[childnum].pid = 0;
310e38
+    if (slot != -1) { /* child had a scoreboard slot? */
310e38
+        process_score *ps = &ap_scoreboard_image->parent[slot];
310e38
+        int i;
310e38
+
310e38
+        pid = ps->pid;
310e38
+        gen = ps->generation;
310e38
+        for (i = 0; i < threads_per_child; i++) {
310e38
+            ap_update_child_status_from_indexes(slot, i, SERVER_DEAD, NULL);
310e38
+        }
310e38
+        ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_EXITED);
310e38
+        if (ps->quiescing != 2) { /* vs perform_idle_server_maintenance() */
310e38
+            retained->active_daemons--;
310e38
+        }
310e38
+        retained->total_daemons--;
310e38
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
310e38
+                     "Child %d stopped: pid %d, gen %d, "
310e38
+                     "active %d/%d, total %d/%d/%d, quiescing %d",
310e38
+                     slot, (int)pid, (int)gen,
310e38
+                     retained->active_daemons, active_daemons_limit,
310e38
+                     retained->total_daemons, retained->max_daemon_used,
310e38
+                     server_limit, ps->quiescing);
310e38
+        ps->not_accepting = 0;
310e38
+        ps->quiescing = 0;
310e38
+        ps->pid = 0;
310e38
     }
310e38
     else {
310e38
         ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED);
310e38
@@ -713,9 +731,19 @@
310e38
 static void event_note_child_started(int slot, pid_t pid)
310e38
 {
310e38
     ap_generation_t gen = retained->mpm->my_generation;
310e38
+
310e38
+    retained->total_daemons++;
310e38
+    retained->active_daemons++;
310e38
     ap_scoreboard_image->parent[slot].pid = pid;
310e38
     ap_scoreboard_image->parent[slot].generation = gen;
310e38
     ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_STARTED);
310e38
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
310e38
+                 "Child %d started: pid %d, gen %d, "
310e38
+                 "active %d/%d, total %d/%d/%d",
310e38
+                 slot, (int)pid, (int)gen,
310e38
+                 retained->active_daemons, active_daemons_limit,
310e38
+                 retained->total_daemons, retained->max_daemon_used,
310e38
+                 server_limit);
310e38
 }
310e38
 
310e38
 static const char *event_get_name(void)
310e38
@@ -737,7 +765,7 @@
310e38
     }
310e38
 
310e38
     if (one_process) {
310e38
-        event_note_child_killed(/* slot */ 0, 0, 0);
310e38
+        event_note_child_stopped(/* slot */ 0, 0, 0);
310e38
     }
310e38
 
310e38
     exit(code);
310e38
@@ -2712,8 +2740,8 @@
310e38
 {
310e38
     int pid;
310e38
 
310e38
-    if (slot + 1 > retained->max_daemons_limit) {
310e38
-        retained->max_daemons_limit = slot + 1;
310e38
+    if (slot + 1 > retained->max_daemon_used) {
310e38
+        retained->max_daemon_used = slot + 1;
310e38
     }
310e38
 
310e38
     if (ap_scoreboard_image->parent[slot].pid != 0) {
310e38
@@ -2781,11 +2809,7 @@
310e38
         return -1;
310e38
     }
310e38
 
310e38
-    ap_scoreboard_image->parent[slot].quiescing = 0;
310e38
-    ap_scoreboard_image->parent[slot].not_accepting = 0;
310e38
     event_note_child_started(slot, pid);
310e38
-    retained->active_daemons++;
310e38
-    retained->total_daemons++;
310e38
     return 0;
310e38
 }
310e38
 
310e38
@@ -2805,7 +2829,8 @@
310e38
     }
310e38
 }
310e38
 
310e38
-static void perform_idle_server_maintenance(int child_bucket)
310e38
+static void perform_idle_server_maintenance(int child_bucket,
310e38
+                                            int *max_daemon_used)
310e38
 {
310e38
     int num_buckets = retained->mpm->num_buckets;
310e38
     int idle_thread_count = 0;
310e38
@@ -2821,7 +2846,7 @@
310e38
             /* We only care about child_bucket in this call */
310e38
             continue;
310e38
         }
310e38
-        if (i >= retained->max_daemons_limit &&
310e38
+        if (i >= retained->max_daemon_used &&
310e38
             free_length == retained->idle_spawn_rate[child_bucket]) {
310e38
             /* short cut if all active processes have been examined and
310e38
              * enough empty scoreboard slots have been found
310e38
@@ -2835,6 +2860,13 @@
310e38
             if (ps->quiescing == 1) {
310e38
                 ps->quiescing = 2;
310e38
                 retained->active_daemons--;
310e38
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
310e38
+                             "Child %d quiescing: pid %d, gen %d, "
310e38
+                             "active %d/%d, total %d/%d/%d",
310e38
+                             i, (int)ps->pid, (int)ps->generation,
310e38
+                             retained->active_daemons, active_daemons_limit,
310e38
+                             retained->total_daemons, retained->max_daemon_used,
310e38
+                             server_limit);
310e38
             }
310e38
             for (j = 0; j < threads_per_child; j++) {
310e38
                 int status = ap_scoreboard_image->servers[i][j].status;
310e38
@@ -2863,8 +2895,9 @@
310e38
             free_slots[free_length++] = i;
310e38
         }
310e38
     }
310e38
-
310e38
-    retained->max_daemons_limit = last_non_dead + 1;
310e38
+    if (*max_daemon_used < last_non_dead + 1) {
310e38
+        *max_daemon_used = last_non_dead + 1;
310e38
+    }
310e38
 
310e38
     if (retained->sick_child_detected) {
310e38
         if (had_healthy_child) {
310e38
@@ -2893,6 +2926,10 @@
310e38
         }
310e38
     }
310e38
 
310e38
+    AP_DEBUG_ASSERT(retained->active_daemons <= retained->total_daemons
310e38
+                    && retained->total_daemons <= retained->max_daemon_used
310e38
+                    && retained->max_daemon_used <= server_limit);
310e38
+
310e38
     if (idle_thread_count > max_spare_threads / num_buckets) {
310e38
         /*
310e38
          * Child processes that we ask to shut down won't die immediately
310e38
@@ -2915,13 +2952,12 @@
310e38
                            active_daemons_limit));
310e38
         ap_log_error(APLOG_MARK, APLOG_TRACE5, 0, ap_server_conf,
310e38
                      "%shutting down one child: "
310e38
-                     "active daemons %d / active limit %d / "
310e38
-                     "total daemons %d / ServerLimit %d / "
310e38
-                     "idle threads %d / max workers %d",
310e38
+                     "active %d/%d, total %d/%d/%d, "
310e38
+                     "idle threads %d, max workers %d",
310e38
                      (do_kill) ? "S" : "Not s",
310e38
                      retained->active_daemons, active_daemons_limit,
310e38
-                     retained->total_daemons, server_limit,
310e38
-                     idle_thread_count, max_workers);
310e38
+                     retained->total_daemons, retained->max_daemon_used,
310e38
+                     server_limit, idle_thread_count, max_workers);
310e38
         if (do_kill) {
310e38
             ap_mpm_podx_signal(all_buckets[child_bucket].pod,
310e38
                                AP_MPM_PODX_GRACEFUL);
310e38
@@ -2970,10 +3006,14 @@
310e38
                 else {
310e38
                     ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
310e38
                                  "server is at active daemons limit, spawning "
310e38
-                                 "of %d children cancelled: %d/%d active, "
310e38
-                                 "rate %d", free_length,
310e38
+                                 "of %d children cancelled: active %d/%d, "
310e38
+                                 "total %d/%d/%d, rate %d", free_length,
310e38
                                  retained->active_daemons, active_daemons_limit,
310e38
-                                 retained->idle_spawn_rate[child_bucket]);
310e38
+                                 retained->total_daemons, retained->max_daemon_used,
310e38
+                                 server_limit, retained->idle_spawn_rate[child_bucket]);
310e38
+                    /* reset the spawning rate and prevent its growth below */
310e38
+                    retained->idle_spawn_rate[child_bucket] = 1;
310e38
+                    ++retained->hold_off_on_exponential_spawning;
310e38
                     free_length = 0;
310e38
                 }
310e38
             }
310e38
@@ -2989,12 +3029,13 @@
310e38
                              retained->total_daemons);
310e38
             }
310e38
             for (i = 0; i < free_length; ++i) {
310e38
-                ap_log_error(APLOG_MARK, APLOG_TRACE5, 0, ap_server_conf,
310e38
-                             "Spawning new child: slot %d active / "
310e38
-                             "total daemons: %d/%d",
310e38
-                             free_slots[i], retained->active_daemons,
310e38
-                             retained->total_daemons);
310e38
-                make_child(ap_server_conf, free_slots[i], child_bucket);
310e38
+                int slot = free_slots[i];
310e38
+                if (make_child(ap_server_conf, slot, child_bucket) < 0) {
310e38
+                    continue;
310e38
+                }
310e38
+                if (*max_daemon_used < slot + 1) {
310e38
+                    *max_daemon_used = slot + 1;
310e38
+                }
310e38
             }
310e38
             /* the next time around we want to spawn twice as many if this
310e38
              * wasn't good enough, but not if we've just done a graceful
310e38
@@ -3016,6 +3057,7 @@
310e38
 static void server_main_loop(int remaining_children_to_start)
310e38
 {
310e38
     int num_buckets = retained->mpm->num_buckets;
310e38
+    int max_daemon_used = 0;
310e38
     int child_slot;
310e38
     apr_exit_why_e exitwhy;
310e38
     int status, processed_status;
310e38
@@ -3061,19 +3103,8 @@
310e38
             }
310e38
             /* non-fatal death... note that it's gone in the scoreboard. */
310e38
             if (child_slot >= 0) {
310e38
-                process_score *ps;
310e38
+                event_note_child_stopped(child_slot, 0, 0);
310e38
 
310e38
-                for (i = 0; i < threads_per_child; i++)
310e38
-                    ap_update_child_status_from_indexes(child_slot, i,
310e38
-                                                        SERVER_DEAD, NULL);
310e38
-
310e38
-                event_note_child_killed(child_slot, 0, 0);
310e38
-                ps = &ap_scoreboard_image->parent[child_slot];
310e38
-                if (ps->quiescing != 2)
310e38
-                    retained->active_daemons--;
310e38
-                ps->quiescing = 0;
310e38
-                /* NOTE: We don't dec in the (child_slot < 0) case! */
310e38
-                retained->total_daemons--;
310e38
                 if (processed_status == APEXIT_CHILDSICK) {
310e38
                     /* resource shortage, minimize the fork rate */
310e38
                     retained->idle_spawn_rate[child_slot % num_buckets] = 1;
310e38
@@ -3123,9 +3154,11 @@
310e38
             continue;
310e38
         }
310e38
 
310e38
+        max_daemon_used = 0;
310e38
         for (i = 0; i < num_buckets; i++) {
310e38
-            perform_idle_server_maintenance(i);
310e38
+            perform_idle_server_maintenance(i, &max_daemon_used);
310e38
         }
310e38
+        retained->max_daemon_used = max_daemon_used;
310e38
     }
310e38
 }
310e38
 
310e38
@@ -3213,7 +3246,7 @@
310e38
                                AP_MPM_PODX_RESTART);
310e38
         }
310e38
         ap_reclaim_child_processes(1, /* Start with SIGTERM */
310e38
-                                   event_note_child_killed);
310e38
+                                   event_note_child_stopped);
310e38
 
310e38
         if (!child_fatal) {
310e38
             /* cleanup pid file on normal shutdown */
310e38
@@ -3239,7 +3272,7 @@
310e38
             ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
310e38
                                AP_MPM_PODX_GRACEFUL);
310e38
         }
310e38
-        ap_relieve_child_processes(event_note_child_killed);
310e38
+        ap_relieve_child_processes(event_note_child_stopped);
310e38
 
310e38
         if (!child_fatal) {
310e38
             /* cleanup pid file on normal shutdown */
310e38
@@ -3261,10 +3294,10 @@
310e38
             apr_sleep(apr_time_from_sec(1));
310e38
 
310e38
             /* Relieve any children which have now exited */
310e38
-            ap_relieve_child_processes(event_note_child_killed);
310e38
+            ap_relieve_child_processes(event_note_child_stopped);
310e38
 
310e38
             active_children = 0;
310e38
-            for (index = 0; index < retained->max_daemons_limit; ++index) {
310e38
+            for (index = 0; index < retained->max_daemon_used; ++index) {
310e38
                 if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) {
310e38
                     active_children = 1;
310e38
                     /* Having just one child is enough to stay around */
310e38
@@ -3282,7 +3315,7 @@
310e38
             ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
310e38
                                AP_MPM_PODX_RESTART);
310e38
         }
310e38
-        ap_reclaim_child_processes(1, event_note_child_killed);
310e38
+        ap_reclaim_child_processes(1, event_note_child_stopped);
310e38
 
310e38
         return DONE;
310e38
     }
310e38
@@ -3302,8 +3335,7 @@
310e38
 
310e38
     if (!retained->mpm->is_ungraceful) {
310e38
         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00493)
310e38
-                     AP_SIG_GRACEFUL_STRING
310e38
-                     " received.  Doing graceful restart");
310e38
+                     AP_SIG_GRACEFUL_STRING " received.  Doing graceful restart");
310e38
         /* wake up the children...time to die.  But we'll have more soon */
310e38
         for (i = 0; i < num_buckets; i++) {
310e38
             ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
310e38
@@ -3316,6 +3348,8 @@
310e38
 
310e38
     }
310e38
     else {
310e38
+        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00494)
310e38
+                     "SIGHUP received.  Attempting to restart");
310e38
         /* Kill 'em all.  Since the child acts the same on the parents SIGTERM
310e38
          * and a SIGHUP, we may as well use the same signal, because some user
310e38
          * pthreads are stealing signals from us left and right.
310e38
@@ -3326,9 +3360,7 @@
310e38
         }
310e38
 
310e38
         ap_reclaim_child_processes(1,  /* Start with SIGTERM */
310e38
-                                   event_note_child_killed);
310e38
-        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00494)
310e38
-                     "SIGHUP received.  Attempting to restart");
310e38
+                                   event_note_child_stopped);
310e38
     }
310e38
 
310e38
     return OK;