From fec621ccba7a20c66dcdbe5a0bbf7f6d0f8d5592 Mon Sep 17 00:00:00 2001 From: Colm MacCarthaigh Date: Sat, 27 Aug 2005 23:22:50 +0000 Subject: [PATCH] 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 --- docs/manual/mod/mpm_common.xml | 2 +- server/mpm/worker/mpm.h | 1 + server/mpm/worker/worker.c | 79 +++++++++++++++++++++++++++++++--- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/docs/manual/mod/mpm_common.xml b/docs/manual/mod/mpm_common.xml index 17daf039c4..e1b2493ced 100644 --- a/docs/manual/mod/mpm_common.xml +++ b/docs/manual/mod/mpm_common.xml @@ -163,7 +163,7 @@ will exit. GracefulShutDownTimeout seconds GracefulShutDownTimeout 0 server config -prefork +preforkworker Available in version 2.4 and later diff --git a/server/mpm/worker/mpm.h b/server/mpm/worker/mpm.h index 29c9f452fe..509a80afe2 100644 --- a/server/mpm/worker/mpm.h +++ b/server/mpm/worker/mpm.h @@ -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 diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 904ef4a902..65d986293c 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -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 } }; -- 2.40.0