]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
First pass at the pipe_of_death logic for the prefork MPM. This does
authorRyan Bloom <rbb@apache.org>
Thu, 7 Jun 2001 00:09:16 +0000 (00:09 +0000)
committerRyan Bloom <rbb@apache.org>
Thu, 7 Jun 2001 00:09:16 +0000 (00:09 +0000)
pass some initial testing, but it needs to be banged on more.  It looks
like if the server gets a lot of requests to restart all at once, there
are potential problems, but other than that this does seem to solve our
current restart issues.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@89282 13f79535-47bb-0310-9956-ffa450edef68

include/httpd.h
include/mpm_common.h
server/mpm/prefork/mpm.h
server/mpm/prefork/prefork.c
server/mpm_common.c

index 9c6edf919c5c8f3befd2b9192a6ca9cb2bef4bcf..0bb3e32e58b1a4cd6428b7a2c29b3f6123177e4f 100644 (file)
@@ -1542,6 +1542,8 @@ const char *ap_strstr_c(const char *s, const char *c);
 
 #endif
 
+#define AP_NORESTART           APR_OS_START_USEERR + 1
+
 #ifdef __cplusplus
 }
 #endif
index 1873dd9778a81cec36a413d337a034ac38d5caac..f029225320c821d3dee819efa8eaeefbeac2623d 100644 (file)
@@ -164,6 +164,49 @@ AP_DECLARE(gid_t) ap_gname2id(const char *name);
 
 #define AP_MPM_HARD_LIMITS_FILE APACHE_MPM_DIR "/mpm_default.h"
 
+#ifdef AP_MPM_USES_POD
+
+typedef struct ap_pod_t ap_pod_t;
+
+struct ap_pod_t {
+    apr_file_t *pod_in;
+    apr_file_t *pod_out;
+    apr_pool_t *p;
+};
+
+/**
+ * Open the pipe-of-death.  The pipe of death is used to tell all child
+ * processes that it is time to die gracefully.
+ * @param p The pool to use for allocating the pipe
+ */
+AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod);
+
+/**
+ * Check the pipe to determine if the process has been signalled to die.
+ */
+AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod);
+
+/**
+ * Close the pipe-of-death
+ */
+AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod);
+
+/**
+ * Write data to the pipe-of-death, signalling that one child process
+ * should die.
+ * @param p The pool to use when allocating any required structures.
+ */
+AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod);
+
+/**
+ * Write data to the pipe-of-death, signalling that all child process
+ * should die.
+ * @param p The pool to use when allocating any required structures.
+ * @param num The number of child processes to kill
+ */
+AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t *pod, int num);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
index 6a923b063637244d3ec63e26adf03f788e0c01df..efad24f9eba470542318b0c5a448820e242fb204 100644 (file)
@@ -68,6 +68,7 @@
 
 #define MPM_NAME "Prefork"
 
+#define AP_MPM_USES_POD 1
 #define AP_MPM_NEEDS_RECLAIM_CHILD_PROCESSES 1
 #define MPM_SYNC_CHILD_TABLE() (ap_sync_scoreboard_image())
 #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
index dcd8135c3345fda1803bd6ea0dbb31554d790d26..f3b1d13a93e67e46af77f49b3c08254f6ca7973b 100644 (file)
@@ -140,6 +140,8 @@ static int ap_daemons_min_free=0;
 static int ap_daemons_max_free=0;
 static int ap_daemons_limit=0;
 
+static ap_pod_t *pod;
+
 /*
  * The max child slot ever assigned, preserved across restarts.  Necessary
  * to deal with MaxClients changes across SIGWINCH restarts.  We use this
@@ -182,6 +184,8 @@ int tpf_child = 0;
 char tpf_server_name[INETD_SERVNAME_LENGTH+1];
 #endif /* TPF */
 
+int die_now = 0;
+
 #ifdef GPROF
 /* 
  * change directory for gprof to plop the gmon.out file
@@ -232,7 +236,7 @@ static void clean_child_exit(int code)
     if (pchild) {
        apr_pool_destroy(pchild);
     }
-    ap_scoreboard_image->parent[my_child_num].process_status = SB_WORKING;
+    ap_mpm_pod_close(pod);
     chdir_for_gprof();
     exit(code);
 }
@@ -378,15 +382,7 @@ static void just_die(int sig)
 static void please_die_gracefully(int sig)
 {
     /* clean_child_exit(0); */
-    ap_scoreboard_image->parent[my_child_num].process_status = SB_IDLE_DIE;
-    if (sig == SIGHUP) {
-        (void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num),
-                                      SERVER_GRACEFUL, (request_rec *) NULL);
-    }
-    else {
-        (void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num),
-                                      SERVER_IDLE_KILL, (request_rec *) NULL);
-    }
+    die_now = 1;
 }
 
 /* volatile just in case */
@@ -414,9 +410,11 @@ static void restart(int sig)
        return;
     }
     restart_pending = 1;
+#if 0
     if ((is_graceful = (sig == SIGWINCH))) {
         apr_pool_cleanup_kill(pconf, NULL, ap_cleanup_scoreboard);
     }
+#endif
 }
 
 static void set_signals(void)
@@ -479,12 +477,9 @@ static void set_signals(void)
 
     /* we want to ignore HUPs and WINCH while we're busy processing one */
     sigaddset(&sa.sa_mask, SIGHUP);
-    sigaddset(&sa.sa_mask, SIGWINCH);
     sa.sa_handler = restart;
     if (sigaction(SIGHUP, &sa, NULL) < 0)
        ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
-    if (sigaction(SIGWINCH, &sa, NULL) < 0)
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGWINCH)");
 #else
     if (!one_process) {
        apr_signal(SIGSEGV, sig_coredump);
@@ -513,7 +508,7 @@ static void set_signals(void)
     apr_signal(SIGHUP, restart);
 #endif /* SIGHUP */
 #ifdef SIGWINCH
-    apr_signal(SIGWINCH, restart);
+    apr_signal(SIGWINCH, SIG_IGN);
 #endif /* SIGWINCH */
 #ifdef SIGPIPE
     apr_signal(SIGPIPE, SIG_IGN);
@@ -533,9 +528,6 @@ static apr_socket_t *csd;
 static int requests_this_child;
 static fd_set main_fds;
 
-#define I_AM_TO_SHUTDOWN()                                                   \
-(ap_scoreboard_image->parent[my_child_num].process_status != SB_WORKING)
-   
 int ap_graceful_stop_signalled(void)
 {
     /* not ever called anymore... */
@@ -581,12 +573,11 @@ static void child_main(int child_num_arg)
     apr_signal(SIGHUP, please_die_gracefully);
 
     ap_sync_scoreboard_image();
-    while (!I_AM_TO_SHUTDOWN()) {
+    while (!die_now) {
 
        /* Prepare to receive a SIGWINCH due to graceful restart so that
         * we can exit cleanly.
         */
-       apr_signal(SIGWINCH, please_die_gracefully);
         apr_signal(SIGTERM, just_die);
 
        /*
@@ -669,13 +660,20 @@ static void child_main(int child_num_arg)
             */
            for (;;) {
                 ap_sync_scoreboard_image();
-               if (I_AM_TO_SHUTDOWN()) {
+               if (die_now) {
                    /* we didn't get a socket, and we were told to die */
                    clean_child_exit(0);
                }
                stat = apr_accept(&csd, sd, ptrans);
                if (stat == APR_SUCCESS || !APR_STATUS_IS_EINTR(stat))
                    break;
+                /* In reality, this could be done later, but to keep it
+                 * consistent with MPMs that have a thread race-condition,
+                 * we will do it here.
+                 */
+                if (!ap_mpm_pod_check(pod)) {
+                    die_now = 1;
+                }
            }
 
            if (stat == APR_SUCCESS)
@@ -777,19 +775,13 @@ static void child_main(int child_num_arg)
            }
 
             ap_sync_scoreboard_image();
-           if (I_AM_TO_SHUTDOWN()) {
+           if (die_now) {
                clean_child_exit(0);
            }
        }
 
        SAFE_ACCEPT(accept_mutex_off());        /* unlock after "accept" */
 
-       /* We've got a socket, let's at least process one request off the
-        * socket before we accept a graceful restart request.  We set
-        * the signal to ignore because we don't want to disturb any
-        * third party code.
-        */
-       apr_signal(SIGWINCH, SIG_IGN);
        /*
         * We now have a connection, so set it up with the appropriate
         * socket options, file descriptors, and read/write buffers.
@@ -894,7 +886,6 @@ static int make_child(server_rec *s, int slot)
         * requested there's no race condition here.
         */
        apr_signal(SIGHUP, please_die_gracefully);
-       apr_signal(SIGWINCH, please_die_gracefully);
        apr_signal(SIGTERM, just_die);
         ap_scoreboard_image->parent[slot].process_status = SB_WORKING;
        child_main(slot);
@@ -940,7 +931,7 @@ static int idle_spawn_rate = 1;
 #endif
 static int hold_off_on_exponential_spawning;
 
-static void perform_idle_server_maintenance(void)
+static void perform_idle_server_maintenance(apr_pool_t *p)
 {
     int i;
     int to_kill;
@@ -1002,7 +993,7 @@ static void perform_idle_server_maintenance(void)
         * shut down gracefully, in case it happened to pick up a request
         * while we were counting
         */
-       kill(ap_scoreboard_image->parent[to_kill].pid, SIGWINCH);
+       ap_mpm_pod_signal(pod);
        idle_spawn_rate = 1;
     }
     else if (idle_count < ap_daemons_min_free) {
@@ -1088,6 +1079,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
 {
     int index;
     int remaining_children_to_start;
+    apr_status_t rv;
 
     pconf = _pconf;
 
@@ -1099,6 +1091,11 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
        /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
        return 1;
     }
+    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(accept_mutex_init(pconf));
     if (!is_graceful) {
@@ -1209,7 +1206,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
            continue;
        }
 
-       perform_idle_server_maintenance();
+       perform_idle_server_maintenance(pconf);
 #ifdef TPF
     shutdown_pending = os_check_server(tpf_server_name);
     ap_check_signals();
@@ -1244,7 +1241,6 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
 
     /* we've been told to restart */
     apr_signal(SIGHUP, SIG_IGN);
-    apr_signal(SIGWINCH, SIG_IGN);
     if (one_process) {
        /* not worth thinking about */
        return 1;
@@ -1264,12 +1260,11 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
 
     if (is_graceful) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
-                   "SIGWINCH received.  Doing graceful restart");
+                   "Graceful restart requested, doing restart");
 
        /* kill off the idle ones */
-       if (unixd_killpg(getpgrp(), SIGWINCH) < 0) {
-           ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGWINCH");
-       }
+        ap_mpm_pod_killpg(pod, ap_daemons_limit);
+
 #ifndef SCOREBOARD_FILE
        /* This is mostly for debugging... so that we know what is still
            * gracefully dealing with existing request.  But we can't really
index edf28680d99eb390428b67c6b51055b68547a586..8a9fcf3fa3904ae1964c1bc9bad306f3b1305a6e 100644 (file)
@@ -78,6 +78,7 @@
 #include "mpm.h"
 #include "mpm_common.h"
 #include "ap_mpm.h"
+#include "ap_listen.h"
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
@@ -338,4 +339,87 @@ int initgroups(const char *name, gid_t basegid)
 }
 #endif /* def NEED_INITGROUPS */
 
+#ifdef AP_MPM_USES_POD
 
+AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
+{
+    apr_status_t rv;
+
+    *pod = apr_palloc(p, sizeof(**pod));
+    rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p);
+    apr_file_pipe_timeout_set((*pod)->pod_out, 0);
+    (*pod)->p = p;
+    return rv;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod)
+{
+    char c;
+    apr_size_t len = 1;
+    apr_status_t rv;
+
+    rv = apr_file_read(pod->pod_in, &c, &len);
+
+    if ((rv == APR_SUCCESS) && (len == 1)) {
+        return APR_SUCCESS;
+    }
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+    return AP_NORESTART;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
+{
+    apr_status_t rv;
+
+    rv = apr_file_close(pod->pod_out);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+
+    rv = apr_file_close(pod->pod_in);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+    return rv;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod)
+{
+    apr_socket_t *sock;
+    apr_sockaddr_t *sa;
+    apr_status_t rv;
+    char char_of_death = '!';
+    apr_size_t one = 1;
+
+    do {
+        if ((rv = apr_file_write(pod->pod_out, &char_of_death, &one))
+                                 != APR_SUCCESS) {
+            if (APR_STATUS_IS_EINTR(rv)) {
+                continue;
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
+                             "write pipe_of_death");
+                return rv;
+            }
+        }
+    } while (1);
+    
+    apr_sockaddr_info_get(&sa, "127.0.0.1", APR_UNSPEC, ap_listeners->bind_addr->port, 0, pod->p);
+    apr_socket_create(&sock, sa->family, SOCK_STREAM, pod->p);
+    apr_connect(sock, sa);    
+    apr_socket_close(sock);
+
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t *pod, int num)
+{
+    int i;
+    for (i = 0; i < num; i++) {
+        ap_mpm_pod_signal(pod);
+    }
+}
+#endif