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

Implement a graceful-stop for the worker MPM. We close our listeners, and then
ask each child process to do the same. We then monitor until all children have
exited.

The change to ap_start_shutdown(void) to ap_start_shutdown(int) may look like
an external API change, but the function is defined static, and used only in
one place.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@241819 13f79535-47bb-0310-9956-ffa450edef68
parent 799699eb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -163,7 +163,7 @@ will exit.</description>
<syntax>GracefulShutDownTimeout <var>seconds</var></syntax>
<default>GracefulShutDownTimeout 0</default>
<contextlist><context>server config</context></contextlist>
<modulelist><module>prefork</module></modulelist>
<modulelist><module>prefork</module><module>worker</module></modulelist>
<compatibility>Available in version 2.4 and later</compatibility>

<usage>
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#define AP_MPM_WANT_SIGNAL_SERVER
#define AP_MPM_WANT_SET_MAX_MEM_FREE
#define AP_MPM_WANT_SET_STACKSIZE
#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER
#define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK

+73 −6
Original line number Diff line number Diff line
@@ -374,7 +374,7 @@ ap_generation_t volatile ap_my_generation;
 * child to force an exit) and so do an exit anyway.
 */

static void ap_start_shutdown(void)
static void ap_start_shutdown(int graceful)
{
    mpm_state = AP_MPMQ_STOPPING;
    if (shutdown_pending == 1) {
@@ -385,6 +385,7 @@ static void ap_start_shutdown(void)
        return;
    }
    shutdown_pending = 1;
    is_graceful = graceful;
}

/* do a graceful restart if graceful == 1 */
@@ -401,7 +402,7 @@ static void ap_start_restart(int graceful)

static void sig_term(int sig)
{
    ap_start_shutdown();
    ap_start_shutdown(sig == AP_SIG_GRACEFUL_STOP);
}

static void restart(int sig)
@@ -427,6 +428,11 @@ static void set_signals(void)
    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, 
@@ -479,6 +485,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_STOP */
#ifdef SIGPIPE
    apr_signal(SIGPIPE, SIG_IGN);
#endif /* SIGPIPE */
@@ -1714,11 +1723,9 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
    server_main_loop(remaining_children_to_start);
    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...
         * (By "gracefully" we don't mean graceful in the same sense as 
         * "apachectl graceful" where we allow old connections to finish.)
         */
        ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
        ap_reclaim_child_processes(1);                /* Start with SIGTERM */
@@ -1736,6 +1743,63 @@ 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 gracefully shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        int active_children;
        int index;
        apr_time_t cutoff = 0;

        /* Close our listeners, and then ask our children to do same */
        ap_close_listeners();
        ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE);
        ap_relieve_child_processes();

        if (!child_fatal) {
            /* cleanup pid file on normal shutdown */
            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 SIGTERM, shutting down");
        }

        /* Don't really exit until each child has finished */
        shutdown_pending = 0;
        do {
            /* Pause for a second */
            apr_sleep(apr_time_from_sec(1));
                
            /* Relieve any children which have now exited */
            ap_relieve_child_processes();
            
            active_children = 0;
            for (index = 0; index < ap_daemons_limit; ++index) {
                if (MPM_CHILD_PID(index) != 0) {
                    if (kill(MPM_CHILD_PID(index), 0) == 0) {
                            active_children = 1;
                            /* Having just one child is enough to stay around */
                            break;
                    }
                }
            }
        } while (!shutdown_pending && active_children &&
                 (!ap_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.
         */
        ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
        ap_reclaim_child_processes(1);

        return 1;
    }

@@ -2150,6 +2214,9 @@ AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
  "Maximum number of child processes for this run of Apache"),
AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
  "Maximum number of worker threads per child process for this run of Apache - Upper limit for ThreadsPerChild"),
AP_INIT_TAKE1("GracefulShutdownTimeout", ap_mpm_set_graceful_shutdown, NULL, 
              RSRC_CONF, "Time in seconds to wait for child processes to " 
              "complete transactions during shutdown"),
{ NULL }
};