From 8ce1d422aa46ea2031e18a5a63f83435c9257500 Mon Sep 17 00:00:00 2001 From: Greg Ames Date: Mon, 11 Feb 2002 23:20:16 +0000 Subject: [PATCH] if a child detects a resource shortage on accept(), limit the rate of fork()s to 1 per second until the situation clears up. Inspired by: Martin Kraemer git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@93366 13f79535-47bb-0310-9956-ffa450edef68 --- include/httpd.h | 6 ++++++ server/mpm/prefork/prefork.c | 13 ++++++++++--- server/mpm/worker/worker.c | 17 ++++++++++++----- server/mpm_common.c | 24 ++++++++++++++++-------- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/include/httpd.h b/include/httpd.h index 800ea082ec..254277f984 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -339,6 +339,12 @@ extern "C" { #define APEXIT_INIT 0x2 /** The child died during its init sequence */ #define APEXIT_CHILDINIT 0x3 +/** + * The child exited due to a resource shortage. + * The parent should limit the rate of forking until + * the situation is resolved. + */ +#define APEXIT_CHILDSICK 0x7 /** * A fatal error, resulting in the whole server aborting. * If a child exits with this error, the parent process diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index a0cf5b9238..f7c510d9d1 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -1037,7 +1037,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) while (!restart_pending && !shutdown_pending) { int child_slot; apr_exit_why_e exitwhy; - int status; + int status, processed_status; /* this is a memory leak, but I'll fix it later. */ apr_proc_t pid; @@ -1048,7 +1048,8 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) * extra child */ if (pid.pid != -1) { - if (ap_process_child_status(&pid, exitwhy, status) == APEXIT_CHILDFATAL) { + processed_status = ap_process_child_status(&pid, exitwhy, status); + if (processed_status == APEXIT_CHILDFATAL) { return 1; } @@ -1058,7 +1059,13 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if (child_slot >= 0) { (void) ap_update_child_status_from_indexes(child_slot, 0, SERVER_DEAD, (request_rec *) NULL); - if (remaining_children_to_start + if (processed_status == APEXIT_CHILDSICK) { + /* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc) + * cut the fork rate to the minimum + */ + idle_spawn_rate = 1; + } + else if (remaining_children_to_start && child_slot < ap_daemons_limit) { /* we're still doing a 1-for-1 replacement of dead * children with new children diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 23733f6f93..01607843ff 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -169,6 +169,7 @@ static int dying = 0; static int workers_may_exit = 0; static int requests_this_child; static int num_listensocks = 0; +static int resource_shortage = 0; static fd_queue_t *worker_queue; /* The structure used to pass unique initialization info to each thread */ @@ -675,6 +676,8 @@ static void *listener_thread(apr_thread_t *thd, void * dummy) rv = lr->accept_func(&csd, lr, ptrans); if (rv == APR_EGENERAL) { + /* E[NM]FILE, ENOMEM, etc */ + resource_shortage = 1; signal_workers(); } if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex))) @@ -965,7 +968,7 @@ static void child_main(int child_num_arg) free(threads); - clean_child_exit(0); + clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0); } static int make_child(server_rec *s, int slot) @@ -1198,7 +1201,7 @@ static void server_main_loop(int remaining_children_to_start) { int child_slot; apr_exit_why_e exitwhy; - int status; + int status, processed_status; apr_proc_t pid; int i; @@ -1206,8 +1209,8 @@ static void server_main_loop(int remaining_children_to_start) ap_wait_or_timeout(&exitwhy, &status, &pid, pconf); if (pid.pid != -1) { - if (ap_process_child_status(&pid, exitwhy, - status) == APEXIT_CHILDFATAL) { + processed_status = ap_process_child_status(&pid, exitwhy, status); + if (processed_status == APEXIT_CHILDFATAL) { shutdown_pending = 1; child_fatal = 1; return; @@ -1221,7 +1224,11 @@ static void server_main_loop(int remaining_children_to_start) ap_scoreboard_image->parent[child_slot].pid = 0; ap_scoreboard_image->parent[child_slot].quiescing = 0; - if (remaining_children_to_start + if (processed_status == APEXIT_CHILDSICK) { + /* resource shortage, minimize the fork rate */ + idle_spawn_rate = 1; + } + else if (remaining_children_to_start && child_slot < ap_daemons_limit) { /* we're still doing a 1-for-1 replacement of dead * children with new children diff --git a/server/mpm_common.c b/server/mpm_common.c index 999ccf32f5..33642d6acf 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -237,15 +237,23 @@ int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status) * we should simply bail out. The caller needs to * check for bad rc from us and exit, running any * appropriate cleanups. + * + * If the child died due to a resource shortage, + * the parent should limit the rate of forking */ - if ((APR_PROC_CHECK_EXIT(why)) && - (status == APEXIT_CHILDFATAL)) { - ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, 0, ap_server_conf, - "Child %" APR_OS_PROC_T_FMT - " returned a Fatal error..." APR_EOL_STR - "Apache is exiting!", - pid->pid); - return APEXIT_CHILDFATAL; + if (APR_PROC_CHECK_EXIT(why)) { + if (status == APEXIT_CHILDSICK) { + return status; + } + if (status == APEXIT_CHILDFATAL) { + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, 0, ap_server_conf, + "Child %" APR_OS_PROC_T_FMT + " returned a Fatal error..." APR_EOL_STR + "Apache is exiting!", + pid->pid); + return APEXIT_CHILDFATAL; + } + return 0; } if (APR_PROC_CHECK_SIGNALED(why)) { -- 2.40.0