Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions server/mpm/event/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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}
};
Expand Down
47 changes: 45 additions & 2 deletions server/mpm/prefork/prefork.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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,
Expand All @@ -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 }
};
Expand Down
62 changes: 49 additions & 13 deletions server/mpm/worker/worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand All @@ -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 }
};
Expand Down