You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by co...@apache.org on 2005/08/26 18:09:58 UTC

svn commit: r240270 - in /httpd/httpd/trunk: CHANGES server/mpm/prefork/mpm.h server/mpm/prefork/prefork.c

Author: colm
Date: Fri Aug 26 09:09:54 2005
New Revision: 240270

URL: http://svn.apache.org/viewcvs?rev=240270&view=rev
Log:

Implement a "graceful-stop" for the prefork MPM (might aswell do the hard one
first). 

General approach is to send SIGUSR1 to all children (which will de-listen, and
exit when finished), and to gather all children as they exit. 

We don't use a sleep(timeout) for the timeout implementation, because this
would lead to a rut of defunct children until the timeout had expired.

set_graceful_shutdown stolen from Ken Coar. See <3E...@Golux.Com>
(28 Mar 2003).


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/server/mpm/prefork/mpm.h
    httpd/httpd/trunk/server/mpm/prefork/prefork.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewcvs/httpd/httpd/trunk/CHANGES?rev=240270&r1=240269&r2=240270&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Fri Aug 26 09:09:54 2005
@@ -2,6 +2,10 @@
 Changes with Apache 2.3.0
   [Remove entries to the current 2.0 and 2.2 section below, when backported]
 
+  *) prefork: Support a graceful-stop procedure. Server will wait until
+     existing requests are finished or until "GracefulShutdownTimeout" 
+     before exiting. [Colm MacCarthaigh, Ken Coar]
+
   *) mod_cgid: Append .PID to the script socket filename and remove the
      script socket on exit. [Colm MacCarthaigh]
 

Modified: httpd/httpd/trunk/server/mpm/prefork/mpm.h
URL: http://svn.apache.org/viewcvs/httpd/httpd/trunk/server/mpm/prefork/mpm.h?rev=240270&r1=240269&r2=240270&view=diff
==============================================================================
--- httpd/httpd/trunk/server/mpm/prefork/mpm.h (original)
+++ httpd/httpd/trunk/server/mpm/prefork/mpm.h Fri Aug 26 09:09:54 2005
@@ -39,6 +39,7 @@
 #define AP_MPM_WANT_SET_MAX_MEM_FREE
 #define AP_MPM_WANT_FATAL_SIGNAL_HANDLER
 #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
+#define AP_MPM_SUPPORTS_GRACEFUL_STOP
 
 #define AP_MPM_USES_POD 1
 #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)

Modified: httpd/httpd/trunk/server/mpm/prefork/prefork.c
URL: http://svn.apache.org/viewcvs/httpd/httpd/trunk/server/mpm/prefork/prefork.c?rev=240270&r1=240269&r2=240270&view=diff
==============================================================================
--- httpd/httpd/trunk/server/mpm/prefork/prefork.c (original)
+++ httpd/httpd/trunk/server/mpm/prefork/prefork.c Fri Aug 26 09:09:54 2005
@@ -96,6 +96,7 @@
 static int ap_daemons_min_free=0;
 static int ap_daemons_max_free=0;
 static int ap_daemons_limit=0;      /* MaxClients */
+static int graceful_shutdown_timeout = 0;
 static int server_limit = DEFAULT_SERVER_LIMIT;
 static int first_server_limit;
 static int changed_limit_at_restart;
@@ -334,6 +335,9 @@
 static void stop_listening(int sig)
 {
     ap_close_listeners();
+
+    /* For a graceful stop, we want the child to exit when done */
+    die_now = 1;
 }
 
 /* volatile just in case */
@@ -351,6 +355,7 @@
         return;
     }
     shutdown_pending = 1;
+    is_graceful = (sig == AP_SIG_GRACEFUL_STOP);
 }
 
 /* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL
@@ -383,6 +388,11 @@
     sa.sa_handler = sig_term;
     if (sigaction(SIGTERM, &sa, NULL) < 0)
         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+#ifdef AP_SIG_GRACEFUL_STOP
+    if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, 
+                     "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")");
+#endif
 #ifdef SIGINT
     if (sigaction(SIGINT, &sa, NULL) < 0)
         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
@@ -430,6 +440,9 @@
 #ifdef AP_SIG_GRACEFUL
     apr_signal(AP_SIG_GRACEFUL, restart);
 #endif /* AP_SIG_GRACEFUL */
+#ifdef AP_SIG_GRACEFUL_STOP
+    apr_signal(AP_SIG_GRACEFUL_STOP, sig_term);
+#endif /* AP_SIG_GRACEFUL */
 #ifdef SIGPIPE
     apr_signal(SIGPIPE, SIG_IGN);
 #endif /* SIGPIPE */
@@ -1074,8 +1087,8 @@
 
     mpm_state = AP_MPMQ_STOPPING;
 
-    if (shutdown_pending) {
-        /* Time to gracefully shut down:
+    if (shutdown_pending && !is_graceful) {
+        /* Time to shut down:
          * Kill child processes, tell them to call child_exit, etc...
          */
         if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
@@ -1096,6 +1109,103 @@
 
         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                     "caught SIGTERM, shutting down");
+
+        return 1;
+    } else if (shutdown_pending) {
+        /* Time to perform a graceful shut down:
+         * Reap the inactive children, and ask the active ones
+         * to close their listeners, then wait until they are
+         * all done to exit.
+         */
+        int active_children;
+        apr_exit_why_e exitwhy;
+        int status;
+        int child_slot;
+        apr_proc_t pid;
+        apr_time_t cutoff;
+
+        /* Stop listening */
+        ap_close_listeners();
+
+        /* kill off the idle ones */
+        ap_mpm_pod_killpg(pod, ap_max_daemons_limit);
+
+        /* Send SIGUSR1 to the active children */
+        active_children = 0;
+        for (index = 0; index < ap_daemons_limit; ++index) {
+            if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) {
+                ap_scoreboard_image->servers[index][0].status = SERVER_GRACEFUL;
+                /* Ask each child to close its listeners. */
+                kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL);
+                active_children++;
+            }
+        }
+
+        /* Allow each child which actually finished to exit */
+        for (; active_children; active_children--) {
+            ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
+            
+            if (pid.pid != -1) {
+                child_slot = find_child_by_pid(&pid);
+                if (child_slot >= 0) {
+                    (void) ap_update_child_status_from_indexes(child_slot, 0, 
+                                            SERVER_DEAD, (request_rec *) NULL);
+                }
+            }
+        }
+
+        /* cleanup pid file */
+        {
+            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=%ld)",
+                                pidfile, (long)getpid());
+        }
+
+        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+           "caught " AP_SIG_GRACEFUL_STOP_STRING ", shutting down gracefully");
+
+        if (graceful_shutdown_timeout) {
+            cutoff = apr_time_now() + 
+                     apr_time_from_sec(graceful_shutdown_timeout);
+        }
+
+        /* Don't really exit until each child has finished */
+        shutdown_pending = 0;
+        do {
+            active_children = 0;
+            for (index = 0; index < ap_daemons_limit; ++index) {
+                if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) {
+                    if (kill(ap_scoreboard_image->parent[index].pid, 0) == 0) {
+                        active_children++;
+                    }
+                }
+            }
+
+            for (index = 0; index < active_children; ++index) {
+                /* Gather any now finished children */
+                ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
+
+                if (pid.pid != -1) {
+                    child_slot = find_child_by_pid(&pid);
+                    if (child_slot >= 0) {
+                        (void) ap_update_child_status_from_indexes(child_slot, 
+                                         0, SERVER_DEAD, (request_rec *) NULL);
+                    }
+                }
+            }
+        } while (!shutdown_pending && active_children &&
+                 (!graceful_shutdown_timeout || apr_time_now() < cutoff));
+
+        /* We might be here because we received SIGTERM, either
+         * way, try and make sure that all of our processes are
+         * really dead.
+         */
+        unixd_killpg(getpgrp(), SIGTERM);
+
         return 1;
     }
 
@@ -1363,6 +1473,16 @@
     return NULL;
 }
 
+static const char *set_graceful_shutdown(cmd_parms *cmd, void *dummy, const char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+    graceful_shutdown_timeout = atoi(arg);
+    return NULL;
+}
+
 static const command_rec prefork_cmds[] = {
 UNIX_DAEMON_COMMANDS,
 LISTEN_COMMANDS,
@@ -1376,6 +1496,9 @@
               "Maximum number of children alive at the same time"),
 AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
               "Maximum value of MaxClients for this run of Apache"),
+AP_INIT_TAKE1("GracefulShutdownTimeout", set_graceful_shutdown, NULL, RSRC_CONF,
+              "Time in seconds to wait for child processes to complete " 
+              "transactions during shutdown"),
 { NULL }
 };
 



Re: svn commit: r240270 - in /httpd/httpd/trunk: CHANGES server/mpm/prefork/mpm.h server/mpm/prefork/prefork.c

Posted by Colm MacCarthaigh <co...@stdlib.net>.
I decided to do prefork first, since it seems the more complicated out
of the MPM's to stop gracefully. I've tested locally, using

	#!/bin/sh

	echo "Content-type: text/plain"
	echo

	while true; do
		sleep 1
		date
	done

as my ultra-sophisticated long-lived download, however I would be amazed
if running two instances of httpd concurrently does not break some
things (esp third party modules which use some kind of lockfile or
on-disk state). So, heads up there on any trunk-runners.

I'm going to add yet more documentation to stopping.xml, to describe
those kind of issues.

On Fri, Aug 26, 2005 at 04:09:58PM -0000, colm@apache.org wrote:
> Author: colm
> Date: Fri Aug 26 09:09:54 2005
> New Revision: 240270
> 
> URL: http://svn.apache.org/viewcvs?rev=240270&view=rev
> Log:
> 
> Implement a "graceful-stop" for the prefork MPM (might aswell do the hard one
> first). 
> 
> General approach is to send SIGUSR1 to all children (which will de-listen, and
> exit when finished), and to gather all children as they exit. 
> 
> We don't use a sleep(timeout) for the timeout implementation, because this
> would lead to a rut of defunct children until the timeout had expired.
> 
> set_graceful_shutdown stolen from Ken Coar. See <3E...@Golux.Com>
> (28 Mar 2003).
> 
> 
> Modified:
>     httpd/httpd/trunk/CHANGES
>     httpd/httpd/trunk/server/mpm/prefork/mpm.h
>     httpd/httpd/trunk/server/mpm/prefork/prefork.c
-- 
Colm MacCárthaigh                        Public Key: colm+pgp@stdlib.net