diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 403f9a3c939..f685bfd6269 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -199,6 +199,9 @@ static fd_queue_info_t *worker_queue_info; static apr_thread_mutex_t *timeout_mutex; +static int idle_termination_timeout = -1; /* never terminate by default */ +static int idle_termination_remaining; + module AP_MODULE_DECLARE_DATA mpm_event_module; /* forward declare */ @@ -444,6 +447,8 @@ typedef struct event_retained_data { */ int *idle_spawn_rate; int hold_off_on_exponential_spawning; + + int idle_timeout; /* did we time out? */ } event_retained_data; static event_retained_data *retained; @@ -3242,6 +3247,7 @@ static void perform_idle_server_maintenance(int child_bucket, { int num_buckets = retained->mpm->num_buckets; int idle_thread_count = 0; + int total_thread_count = 0; process_score *ps; int free_length = 0; int free_slots[MAX_SPAWN_RATE]; @@ -3292,6 +3298,7 @@ static void perform_idle_server_maintenance(int child_bucket, if (status >= SERVER_READY && status < SERVER_GRACEFUL) { ++child_threads_active; } + ++total_thread_count; } active_thread_count += child_threads_active; if (child_threads_active == threads_per_child) { @@ -3338,6 +3345,21 @@ static void perform_idle_server_maintenance(int child_bucket, && retained->total_daemons <= retained->max_daemon_used && retained->max_daemon_used <= server_limit); + if (idle_termination_timeout >= 0) { + if (idle_thread_count == total_thread_count) { + /* we are completely idle, decrease and check the timer */ + if (--idle_termination_remaining < 0) { + /* the termination timeout has expired, inform us we want to terminate immediately */ + retained->mpm->shutdown_pending = 1; + retained->mpm->is_ungraceful = 1; + retained->idle_timeout = 1; + } + } else { + /* not idle, reset the timer */ + idle_termination_remaining = idle_termination_timeout; + } + } + if (idle_thread_count > max_spare_threads / num_buckets) { /* * Child processes that we ask to shut down won't die immediately @@ -3783,8 +3805,15 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) if (!child_fatal) { /* cleanup pid file on normal shutdown */ ap_remove_pid(pconf, ap_pid_fname); - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, - ap_server_conf, APLOGNO(00491) "caught SIGTERM, shutting down"); + + /* log message depends on if we are terminating by signal or by idle timeout */ + if (!retained->idle_timeout) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00491) + "caught SIGTERM, shutting down"); + } else { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00491) + "idle timeout reached, shutting down"); + } } return DONE; @@ -4481,6 +4510,17 @@ static const char *set_worker_factor(cmd_parms * cmd, void *dummy, return NULL; } +static const char *set_idle_termination_timeout (cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + idle_termination_timeout = atoi(arg); + idle_termination_remaining = idle_termination_timeout; + return NULL; +} static const command_rec event_cmds[] = { LISTEN_COMMANDS, @@ -4504,6 +4544,8 @@ static const command_rec event_cmds[] = { AP_INIT_TAKE1("AsyncRequestWorkerFactor", set_worker_factor, NULL, RSRC_CONF, "How many additional connects will be accepted per idle " "worker thread"), + AP_INIT_TAKE1("IdleTerminationTimeout", set_idle_termination_timeout, NULL, RSRC_CONF, + "Number of seconds to terminate in when the server is idle"), AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, {NULL} }; diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index d9837725c9e..1cc035dbf37 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -95,6 +95,9 @@ static int ap_daemons_max_free=0; static int ap_daemons_limit=0; /* MaxRequestWorkers */ static int server_limit = 0; +static int idle_termination_timeout = -1; /* never terminate by default */ +static int idle_termination_remaining; + typedef struct prefork_child_bucket { ap_pod_t *pod; ap_listen_rec *listeners; @@ -131,6 +134,8 @@ typedef struct prefork_retained_data { #define MAX_SPAWN_RATE (32) #endif int hold_off_on_exponential_spawning; + + int idle_timeout; /* did we time out? */ } prefork_retained_data; static prefork_retained_data *retained; @@ -866,6 +871,22 @@ static void perform_idle_server_maintenance(apr_pool_t *p) } } retained->max_daemons_limit = last_non_dead + 1; + + if (idle_termination_timeout >= 0) { + if (idle_count == total_non_dead) { + /* we are completely idle, decrease and check the timer */ + if (--idle_termination_remaining < 0) { + /* the termination timeout has expired, inform us we want to terminate immediately */ + retained->mpm->shutdown_pending = 1; + retained->mpm->is_ungraceful = 1; + retained->idle_timeout = 1; + } + } else { + /* not idle, reset the timer */ + idle_termination_remaining = idle_termination_timeout; + } + } + if (idle_count > ap_daemons_max_free) { static int bucket_kill_child_record = -1; /* kill off one child... we use the pod because that'll cause it to @@ -1202,8 +1223,15 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* cleanup pid file on normal shutdown */ ap_remove_pid(pconf, ap_pid_fname); - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00169) - "caught SIGTERM, shutting down"); + + /* log message depends on if we are terminating by signal or by idle timeout */ + if (!retained->idle_timeout) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00169) + "caught SIGTERM, shutting down"); + } else { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00169) + "idle timeout reached, shutting down"); + } return DONE; } @@ -1342,6 +1370,7 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp retained->mpm = ap_unixd_mpm_get_retained_data(); retained->mpm->baton = retained; retained->idle_spawn_rate = 1; + retained->idle_timeout = 0; } else if (retained->mpm->baton != retained) { /* If the MPM changes on restart, be ungraceful */ @@ -1571,6 +1600,18 @@ static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *ar return NULL; } +static const char *set_idle_termination_timeout (cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + idle_termination_timeout = atoi(arg); + idle_termination_remaining = idle_termination_timeout; + return NULL; +} + static const command_rec prefork_cmds[] = { LISTEN_COMMANDS, AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF, @@ -1585,6 +1626,8 @@ AP_INIT_TAKE1("MaxRequestWorkers", 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 MaxRequestWorkers for this run of Apache"), +AP_INIT_TAKE1("IdleTerminationTimeout", set_idle_termination_timeout, NULL, RSRC_CONF, + "Number of seconds to terminate in when the server is idle"), AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, { NULL } }; diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 42b81a8ed1b..7e1d39e40b7 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -138,6 +138,9 @@ static fd_queue_t *worker_queue; static fd_queue_info_t *worker_queue_info; static apr_pollset_t *worker_pollset; +static int idle_termination_timeout = -1; /* never terminate by default */ +static int idle_termination_remaining; + typedef struct worker_child_bucket { ap_pod_t *pod; ap_listen_rec *listeners; @@ -175,6 +178,8 @@ typedef struct worker_retained_data { */ int *idle_spawn_rate; int hold_off_on_exponential_spawning; + + int idle_timeout; /* did we time out? */ } worker_retained_data; static worker_retained_data *retained; @@ -1427,23 +1432,17 @@ static void startup_children(int number_to_start) static void perform_idle_server_maintenance(int child_bucket) { int num_buckets = retained->mpm->num_buckets; - int idle_thread_count; + int idle_thread_count = 0; + int total_thread_count = 0; process_score *ps; - int free_length; + int free_length = 0; int totally_free_length = 0; int free_slots[MAX_SPAWN_RATE]; - int last_non_dead; - int total_non_dead; + int last_non_dead = -1; + int total_non_dead = 0; int active_thread_count = 0; int i, j; - /* initialize the free_list */ - free_length = 0; - - idle_thread_count = 0; - last_non_dead = -1; - total_non_dead = 0; - for (i = 0; i < ap_daemons_limit; ++i) { /* Initialization to satisfy the compiler. It doesn't know * that threads_per_child is always > 0 */ @@ -1491,6 +1490,7 @@ static void perform_idle_server_maintenance(int child_bucket) if (status >= SERVER_READY && status < SERVER_GRACEFUL) { ++child_threads_active; } + ++total_thread_count; } } active_thread_count += child_threads_active; @@ -1558,6 +1558,21 @@ static void perform_idle_server_maintenance(int child_bucket) } } + if (idle_termination_timeout >= 0) { + if (idle_thread_count == total_thread_count) { + /* we are completely idle, decrease and check the timer */ + if (--idle_termination_remaining < 0) { + /* the termination timeout has expired, inform us we want to terminate immediately */ + retained->mpm->shutdown_pending = 1; + retained->mpm->is_ungraceful = 1; + retained->idle_timeout = 1; + } + } else { + /* not idle, reset the timer */ + idle_termination_remaining = idle_termination_timeout; + } + } + if (idle_thread_count > max_spare_threads / num_buckets) { /* Kill off one child */ ap_mpm_podx_signal(retained->buckets[child_bucket].pod, @@ -1983,8 +1998,15 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if (!child_fatal) { /* cleanup pid file on normal shutdown */ ap_remove_pid(pconf, ap_pid_fname); - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, - ap_server_conf, APLOGNO(00295) "caught SIGTERM, shutting down"); + + /* log message depends on if we are terminating by signal or by idle timeout */ + if (!retained->idle_timeout) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00295) + "caught SIGTERM, shutting down"); + } else { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00295) + "idle timeout reached, shutting down"); + } } return DONE; } @@ -2484,6 +2506,18 @@ static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *ar return NULL; } +static const char *set_idle_termination_timeout (cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + idle_termination_timeout = atoi(arg); + idle_termination_remaining = idle_termination_timeout; + return NULL; +} + static const command_rec worker_cmds[] = { LISTEN_COMMANDS, AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF, @@ -2502,6 +2536,8 @@ 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("IdleTerminationTimeout", set_idle_termination_timeout, NULL, RSRC_CONF, + "Number of seconds to terminate in when the server is idle"), AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, { NULL } };