This doesn't work currently.  It is posted just in case somebody takes
a look and finds something they disagree with or can offer hints.  It
will serve pages but graceful restart doesn't terminate the old server
process properly and I haven't tried any other interesting scenarios.

The next thing I need to do is to add APR_HAS_THREADS tweaks to the
pod support to avoid checking the pod more than once in a given
process once some thread in the process finds out that it should die.
For this we need to set up an intra-process mutex in each server
process.  ap_mpm_pod_child_open() may be necessary to do that step.  I
don't know how cool it is to inherit a mutex from the parent.

Index: server/mpm/threaded/threaded.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/threaded/threaded.c,v
retrieving revision 1.35
diff -u -r1.35 threaded.c
--- server/mpm/threaded/threaded.c      2001/06/15 18:33:09     1.35
+++ server/mpm/threaded/threaded.c      2001/06/19 17:55:16
@@ -131,10 +131,7 @@
 
 char ap_coredump_dir[MAX_STRING_LEN];
 
-static apr_file_t *pipe_of_death_in = NULL;
-static apr_file_t *pipe_of_death_out = NULL;
-static apr_lock_t *pipe_of_death_mutex;   /* insures that a child process only
-                                             consumes one character */
+static ap_pod_t *pod;
 
 /* *Non*-shared http_main globals... */
 
@@ -170,10 +167,80 @@
 static apr_lock_t *accept_mutex;
 static const char *lock_fname;
 
-#ifdef NO_SERIALIZED_ACCEPT
-#define SAFE_ACCEPT(stmt) APR_SUCCESS
+/* 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)
+{
+    if (pchild) {
+       apr_pool_destroy(pchild);
+    }
+    exit(code);
+}
+
+static void accept_mutex_child_init(apr_pool_t *p)
+{
+    apr_status_t rv;
+
+    rv = apr_lock_child_init(&accept_mutex, lock_fname, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "Couldn't initialize cross-process lock in child");
+        clean_child_exit(APEXIT_CHILDINIT);
+    }
+}
+
+static apr_status_t accept_mutex_init(apr_pool_t *p)
+{
+    apr_status_t rv;
+
+    /* Initialize cross-process accept lock */
+    lock_fname = apr_psprintf(p, "%s.%" APR_OS_PROC_T_FMT,
+                              ap_server_root_relative(p, lock_fname),
+                              ap_my_pid);
+    rv = apr_lock_create(&accept_mutex, APR_MUTEX, APR_LOCKALL,
+                         lock_fname, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "Couldn't create accept lock");
+    }
+    return rv;
+}
+
+static void accept_mutex_on(void)
+{
+    apr_status_t rv;
+
+    rv = apr_lock_acquire(accept_mutex);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "apr_lock_acquire failed. Attempting to shutdown "
+                     "process gracefully.");
+        workers_may_exit = 1;
+    }
+}
+
+static void accept_mutex_off(void)
+{
+    apr_status_t rv;
+
+    rv = apr_lock_release(accept_mutex);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "apr_lock_release failed. Attempting to shutdown "
+                     "process gracefully.");
+        workers_may_exit = 1;
+    }
+}
+
+/* On some architectures it's safe to do unserialized accept()s in the single
+ * Listen case.  But it's never safe to do it in the case where there's
+ * multiple Listen statements.  Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ * when it's safe in the single Listen case.
+ */
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+#define SAFE_ACCEPT(stmt) do {if (ap_listeners->next) {stmt;}} while(0)
 #else
-#define SAFE_ACCEPT(stmt) (stmt)
+#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
 #endif
 
 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
@@ -201,16 +268,6 @@
     return APR_ENOTIMPL;
 }
 
-/* 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)
-{
-    if (pchild) {
-       apr_pool_destroy(pchild);
-    }
-    exit(code);
-}
-
 /* handle all varieties of core dumping signals */
 static void sig_coredump(int sig)
 {
@@ -466,29 +523,6 @@
     }
 }
 
-/* Sets workers_may_exit if we received a character on the pipe_of_death */
-static void check_pipe_of_death(void)
-{
-    apr_lock_acquire(pipe_of_death_mutex);
-    if (!workers_may_exit) {
-        apr_status_t ret;
-        char pipe_read_char;
-       apr_size_t n = 1;
-
-        ret = apr_recv(listensocks[0], &pipe_read_char, &n);
-        if (APR_STATUS_IS_EAGAIN(ret)) {
-            /* It lost the lottery. It must continue to suffer
-             * through a life of servitude. */
-        }
-        else {
-            /* It won the lottery (or something else is very
-             * wrong). Embrace death with open arms. */
-            workers_may_exit = 1;
-        }
-    }
-    apr_lock_release(pipe_of_death_mutex);
-}
-
 static void * worker_thread(void * dummy)
 {
     proc_info * ti = dummy;
@@ -511,8 +545,9 @@
     worker_thread_count++;
     apr_lock_release(worker_thread_count_mutex);
 
-    apr_poll_setup(&pollset, num_listensocks+1, tpool);
-    for(n=0 ; n <= num_listensocks ; ++n)
+    /* XXX noop with SAFE_ACCEPT() */
+    apr_poll_setup(&pollset, num_listensocks, tpool);
+    for (n = 0 ; n < num_listensocks; ++n)
        apr_poll_socket_add(pollset, listensocks[n], APR_POLLIN);
 
     /* TODO: Switch to a system where threads reuse the results from earlier
@@ -525,46 +560,27 @@
 
         (void) ap_update_child_status(process_slot, thread_slot, SERVER_READY, 
                                       (request_rec *) NULL);
-        if ((rv = SAFE_ACCEPT(apr_lock_acquire(accept_mutex)))
-            != APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
-                         "apr_lock_acquire failed. Attempting to shutdown "
-                         "process gracefully.");
-            workers_may_exit = 1;
-        }
 
+        SAFE_ACCEPT(accept_mutex_on());
         while (!workers_may_exit) {
            apr_status_t ret;
            apr_int16_t event;
 
-            ret = apr_poll(pollset, &n, -1);
-            if (ret != APR_SUCCESS) {
-                if (APR_STATUS_IS_EINTR(ret)) {
-                    continue;
-                }
+            if (ap_listeners->next) {
+                /* more than one socket */
+                ret = apr_poll(pollset, &n, -1);
+                if (ret != APR_SUCCESS) {
+                    if (APR_STATUS_IS_EINTR(ret)) {
+                        continue;
+                    }
 
-                /* apr_poll() will only return errors in catastrophic
-                 * circumstances. Let's try exiting gracefully, for now. */
-                ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
+                    /* apr_poll() will only return errors in catastrophic
+                     * circumstances. Let's try exiting gracefully, for now. */
+                    ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
                              ap_server_conf, "apr_poll: (listen)");
-                workers_may_exit = 1;
-            }
-
-            if (workers_may_exit) break;
-
-           apr_poll_revents_get(&event, listensocks[0], pollset);
-            if (event & APR_POLLIN) {
-                /* A process got a signal on the shutdown pipe. Check if we're
-                 * the lucky process to die. */
-                check_pipe_of_death();
-                continue;
-            }
+                    workers_may_exit = 1;
+                }
 
-            if (num_listensocks == 1) {
-                sd = ap_listeners->sd;
-                goto got_fd;
-            }
-            else {
                 /* find a listener */
                 curr_pollfd = last_pollfd;
                 do {
@@ -581,6 +597,11 @@
                     }
                 } while (curr_pollfd != last_pollfd);
             }
+            else {
+                /* only one socket, just pretend we did the other stuff */
+                sd = ap_listeners->sd;
+                goto got_fd;
+            }
         }
     got_fd:
         if (!workers_may_exit) {
@@ -589,26 +610,17 @@
                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, 
                              "apr_accept");
             }
-            if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex)))
-                != APR_SUCCESS) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
-                             "apr_lock_release failed. Attempting to shutdown "
-                             "process gracefully.");
+            SAFE_ACCEPT(accept_mutex_off());
+            if (!ap_mpm_pod_check(pod)) {
                 workers_may_exit = 1;
             }
-            if (csd != NULL) {
+            else if (csd != NULL) {
                 process_socket(ptrans, csd, process_slot, thread_slot);
                 requests_this_child--;
             }
         }
         else {
-            if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex)))
-                != APR_SUCCESS) {
-                ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
-                             "apr_lock_release failed. Attempting to shutdown "
-                             "process gracefully.");
-                workers_may_exit = 1;
-            }
+            SAFE_ACCEPT(accept_mutex_off());
             break;
         }
         apr_pool_clear(ptrans);
@@ -649,21 +661,17 @@
     ap_listen_rec *lr;
     apr_status_t rv;
 
-
     ap_my_pid = getpid();
+
+    /* Get a subpool for global allocations in this child so that we
+     * can have cleanups occur when the child exits.
+     */
     apr_pool_create(&pchild, pconf);
 
     /*stuff to do before we switch id's, so we have permissions.*/
     reopen_scoreboard(pchild);
+    SAFE_ACCEPT(accept_mutex_child_init(pchild));
 
-    rv = SAFE_ACCEPT(apr_lock_child_init(&accept_mutex, lock_fname,
-                     pchild));
-    if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
-                     "Couldn't initialize cross-process lock in child");
-        clean_child_exit(APEXIT_CHILDFATAL);
-    }
-
     if (unixd_setup_child()) {
        clean_child_exit(APEXIT_CHILDFATAL);
     }
@@ -689,12 +697,9 @@
     
     /* Set up the pollfd array */
     listensocks = apr_pcalloc(pchild,
-                           sizeof(*listensocks) * (num_listensocks + 1));
-#if APR_FILES_AS_SOCKETS
-    apr_socket_from_file(&listensocks[0], pipe_of_death_in);
-#endif
-    for (lr = ap_listeners, i = 1; i <= num_listensocks; lr = lr->next, ++i)
-       listensocks[i]=lr->sd;
+                           sizeof(*listensocks) * num_listensocks);
+    for (lr = ap_listeners, i = 0; i < num_listensocks; lr = lr->next, ++i)
+       listensocks[i] = lr->sd;
 
     /* Setup worker threads */
 
@@ -707,8 +712,6 @@
     worker_thread_count = 0;
     apr_lock_create(&worker_thread_count_mutex, APR_MUTEX, APR_INTRAPROCESS,
                     NULL, pchild);
-    apr_lock_create(&pipe_of_death_mutex, APR_MUTEX, APR_INTRAPROCESS, 
-                    NULL, pchild);
     apr_threadattr_create(&thread_attr, pchild);
     apr_threadattr_detach_set(thread_attr, 0);    /* 0 means PTHREAD_CREATE_JOINABLE 
*/
 
@@ -828,29 +831,6 @@
     return 0;
 }
 
-/* If there aren't many connections coming in from the network, the child 
- * processes may need to be awakened from their network i/o waits.
- * The pipe of death is an effective prod.
- */
-   
-static void wake_up_and_die(void) 
-{
-    int i;
-    char char_of_death = '!';
-    apr_size_t one = 1;
-    apr_status_t rv;
-    
-    for (i = 0; i < ap_daemons_limit;) {
-        if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) 
-                                 != APR_SUCCESS) {
-            if (APR_STATUS_IS_EINTR(rv)) continue;
-            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, 
-                         "write pipe_of_death");
-        }
-        i++;
-    }
-}
-
 /* start up a bunch of children */
 static void startup_children(int number_to_start)
 {
@@ -889,8 +869,6 @@
     int free_slots[MAX_SPAWN_RATE];
     int last_non_dead;
     int total_non_dead;
-    apr_size_t one = 1;
-    apr_status_t rv;
 
     /* initialize the free_list */
     free_length = 0;
@@ -943,11 +921,11 @@
     ap_max_daemons_limit = last_non_dead + 1;
 
     if (idle_thread_count > max_spare_threads) {
-        /* Kill off one child */
-        char char_of_death = '!';
-        if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) != 
APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, "write 
pipe_of_death");
-        }
+        /* Kill off one child... we use the pod because that'll cause it to
+         * shut down gracefully, in case it happened to pick up a request
+         * while we were counting
+         */
+        ap_mpm_pod_signal(pod);
         idle_spawn_rate = 1;
     }
     else if (idle_thread_count < min_spare_threads) {
@@ -1067,21 +1045,7 @@
 
     pconf = _pconf;
     ap_server_conf = s;
-    rv = apr_file_pipe_create(&pipe_of_death_in, &pipe_of_death_out, pconf);
-    if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv,
-                     (const server_rec*) ap_server_conf,
-                     "apr_file_pipe_create (pipe_of_death)");
-        exit(1);
-    }
 
-    if ((rv = apr_file_pipe_timeout_set(pipe_of_death_in, 0)) != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv,
-                     (const server_rec*) ap_server_conf,
-                     "apr_file_pipe_timeout_set (pipe_of_death)");
-        exit(1);
-    }
-    ap_server_conf = s;
     if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
         /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
@@ -1090,18 +1054,16 @@
     }
     ap_log_pid(pconf, ap_pid_fname);
 
-    /* Initialize cross-process accept lock */
-    lock_fname = apr_psprintf(_pconf, "%s.%" APR_OS_PROC_T_FMT,
-                             ap_server_root_relative(_pconf, lock_fname),
-                             ap_my_pid);
-    rv = apr_lock_create(&accept_mutex, APR_MUTEX, APR_LOCKALL,
-                   lock_fname, _pconf);
-    if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
-                     "Couldn't create accept lock");
+    if ((rv = ap_mpm_pod_open(pconf, &pod))) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                     "Could not open pipe-of-death.");
         return 1;
     }
 
+    SAFE_ACCEPT(rv = accept_mutex_init(pconf));
+    if (rv != APR_SUCCESS) {
+        return 1;
+    }
 
     if (!is_graceful) {
         ap_create_scoreboard(pconf, SB_SHARED);
@@ -1147,7 +1109,7 @@
         /* Time to gracefully shut down:
          * Kill child processes, tell them to call child_exit, etc...
          */
-        wake_up_and_die();
+        /* wake_up_and_die(); er, this wrote to the POD... why do that *and* send 
+SIGTERM? */
 
         if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg 
SIGTERM");
@@ -1188,13 +1150,16 @@
     update_scoreboard_global();
     
     /* wake up the children...time to die.  But we'll have more soon */
-    wake_up_and_die();
+    /* wake_up_and_die();  er, this wrote to the pod... why do that *and* send 
+SIGTERM? */
     
     if (is_graceful) {
         int i, j;
 
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
                    "SIGWINCH received.  Doing graceful restart");
+
+        /* kill off the server processes gracefully */
+        ap_mpm_pod_killpg(pod, ap_daemons_limit);
 
        /* This is mostly for debugging... so that we know what is still
          * gracefully dealing with existing request.

-- 
Jeff Trawick | [EMAIL PROTECTED] | PGP public key at web site:
       http://www.geocities.com/SiliconValley/Park/9289/
             Born in Roswell... married an alien...

Reply via email to