#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
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;
* 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;
}
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
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 */
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)))
free(threads);
- clean_child_exit(0);
+ clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0);
}
static int make_child(server_rec *s, int slot)
{
int child_slot;
apr_exit_why_e exitwhy;
- int status;
+ int status, processed_status;
apr_proc_t pid;
int i;
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;
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
* 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)) {