Commit c69524da authored by Colm MacCarthaigh's avatar Colm MacCarthaigh
Browse files

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 <3E84B1EC.3050007@Golux.Com>
(28 Mar 2003).



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@240270 13f79535-47bb-0310-9956-ffa450edef68
parent 25da38ba
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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]
+1 −0
Original line number Diff line number Diff line
@@ -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)
+125 −2
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ static int ap_daemons_to_start=0;
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 just_die(int sig)
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 @@ static void sig_term(int sig)
        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 @@ static void set_signals(void)
    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 @@ static void set_signals(void)
#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 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)

    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 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)

        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 @@ static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *ar
    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 @@ AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF,
              "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 }
};