worker.c revision e4d36aa1eb0631a1b696c7a70d696f9c869bddcc
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* ====================================================================
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * The Apache Software License, Version 1.1
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Redistribution and use in source and binary forms, with or without
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * modification, are permitted provided that the following conditions
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 1. Redistributions of source code must retain the above copyright
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * notice, this list of conditions and the following disclaimer.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 2. Redistributions in binary form must reproduce the above copyright
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * notice, this list of conditions and the following disclaimer in
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * the documentation and/or other materials provided with the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * distribution.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 3. The end-user documentation included with the redistribution,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * if any, must include the following acknowledgment:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * "This product includes software developed by the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Apache Software Foundation (http://www.apache.org/)."
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Alternately, this acknowledgment may appear in the software itself,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * if and wherever such third-party acknowledgments normally appear.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 4. The names "Apache" and "Apache Software Foundation" must
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * not be used to endorse or promote products derived from this
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * software without prior written permission. For written
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * permission, please contact apache@apache.org.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 5. Products derived from this software may not be called "Apache",
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * nor may "Apache" appear in their name, without prior written
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * permission of the Apache Software Foundation.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * SUCH DAMAGE.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * ====================================================================
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * This software consists of voluntary contributions made by many
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * individuals on behalf of the Apache Software Foundation. For more
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * information on the Apache Software Foundation, please see
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Portions of this software are based upon public domain software
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * originally written at the National Center for Supercomputing Applications,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * University of Illinois, Urbana-Champaign.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The purpose of this MPM is to fix the design flaws in the threaded
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * model. Because of the way that pthreads and mutex locks interact,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * it is basically impossible to cleanly gracefully shutdown a child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * process if multiple threads are all blocked in accept. This model
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * fixes those problems.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include <sys/processor.h> /* for bindprocessor() */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#error The Worker MPM requires APR threads, but they are unavailable.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Limit on the total --- clients will be locked out if more servers than
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * this are needed. It is intended solely to keep the server from crashing
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * when things get out of hand.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * We keep a hard maximum number of servers, for two reasons --- first off,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * in case something goes seriously wrong, we want to stop the fork bomb
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * short of actually crashing the machine we're running on by filling some
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * kernel table. Secondly, it keeps the size of the scoreboard file small
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * enough that we can read the whole thing without worrying too much about
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * the overhead.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Admin can't tune ServerLimit beyond MAX_SERVER_LIMIT. We want
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * some sort of compile-time limit to help catch typos.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* Limit on the threads per process. Clients will be locked out if more than
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * this * server_limit are needed.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * We keep this for one reason it keeps the size of the scoreboard file small
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * enough that we can read the whole thing without worrying too much about
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * the overhead.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* Admin can't tune ThreadLimit beyond MAX_THREAD_LIMIT. We want
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * some sort of compile-time limit to help catch typos.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Actual definitions of config globals
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversint ap_threads_per_child = 0; /* Worker threads per child */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int dying = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The structure used to pass unique initialization info to each thread */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieverstypedef struct {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Structure used to pass information to the thread responsible for
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * creating the rest of the threads.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieverstypedef struct {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * The max child slot ever assigned, preserved across restarts. Necessary
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * use this value to optimize routines that have to scan the entire
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * scoreboard.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* *Non*-shared http_main globals... */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The worker MPM respects a couple of runtime flags that can aid
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * in debugging. Setting the -DNO_DETACH flag will prevent the root process
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * from detaching from its controlling terminal. Additionally, setting
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * the -DONE_PROCESS flag (which implies -DNO_DETACH) will get you the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * child_main loop running in the process which originally started up.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * This gives you a pretty nice debugging environment. (You'll get a SIGHUP
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * early in standalone_main; just continue through. This is the server
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * trying to kill off any child processes which it might have lying
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * around --- Apache doesn't keep track of their pids, it just sends
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * SIGHUP to the process group, ignoring it in the root process.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Continue through and you'll be fine.).
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int one_process = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_pool_t *pconf; /* Pool for config stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_pool_t *pchild; /* Pool for httpd child stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic pid_t ap_my_pid; /* Linux getpid() doesn't work except in main
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers thread. Use this instead */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Locks for accept serialization */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The LISTENER_SIGNAL signal will be sent from the main thread to the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * listener thread to wake it up for graceful termination (what a child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * process from an old generation does when the admin does "apachectl
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * graceful"). This signal will be blocked in all threads of a child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * process except for the listener thread.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* An array of socket descriptors in use by each thread used to
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * perform a non-graceful (forced) shutdown of the server. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void close_worker_sockets(void)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers for (i = 0; i < ap_threads_per_child; i++) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void wakeup_listener(void)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* XXX there is an obscure path that this doesn't handle perfectly:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * right after listener thread is created but before
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * listener_os_thread is set, the first worker thread hits an
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * error and starts graceful termination
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" on all
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * platforms and wake up the listener thread since it is the only thread
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * with SIGHUP unblocked, but that doesn't work on Linux
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers pthread_kill(*listener_os_thread, LISTENER_SIGNAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* in case we weren't called from the listener thread, wake up the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * listener thread
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* for ungraceful termination, let the workers exit now;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * for graceful termination, the listener thread will notify the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * workers to exit once it has stopped accepting new connections
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers close_worker_sockets(); /* forcefully kill all current connections */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay SieversAP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* a clean exit from a child with proper cleanup */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void clean_child_exit(int code) __attribute__ ((noreturn));
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/*****************************************************************
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Connection structures and accounting...
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* volatile just in case */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int volatile shutdown_pending;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int volatile restart_pending;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int volatile is_graceful;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic volatile int child_fatal;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * functions to initiate shutdown or restart without relying on signals.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Previously this was initiated in sig_term() and restart() signal handlers,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * but we want to be able to start a shutdown/restart from other sources --
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * e.g. on Win32, from the service manager. Now the service manager can
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * these functions can also be called by the child processes, since global
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * variables are no longer used to pass on the required action to the parent.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * These should only be called from the parent process itself, since the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * parent process will use the shutdown_pending and restart_pending variables
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * to determine whether to shutdown or restart. The child process should
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * call signal_parent() directly to tell the parent to die -- this will
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * cause neither of those variable to be set, which the parent will
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * assume means something serious is wrong (which it will be, for the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * child to force an exit) and so do an exit anyway.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void ap_start_shutdown(void)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* Um, is this _probably_ not an error, if the user has
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * tried to do a shutdown twice quickly, so we won't
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * worry about reporting it.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* do a graceful restart if graceful == 1 */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* Probably not an error - don't bother reporting it */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void set_signals(void)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "sigaction(SIGTERM)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGINT)");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGXCPU)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGXFSZ)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGPIPE)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * processing one */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGHUP)");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* SIGXCPU */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* SIGXFSZ */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#endif /* SIGHUP */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* AP_SIG_GRACEFUL */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* SIGPIPE */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/*****************************************************************
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Here follows a long bunch of generic server bookkeeping stuff...
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* XXX this is really a bad confusing obsolete name
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * maybe it should be ap_mpm_process_exiting?
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* note: for a graceful termination, listener_may_exit will be set before
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * workers_may_exit, so check listener_may_exit
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/*****************************************************************
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Child process main loop.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int my_thread_num, apr_bucket_alloc_t *bucket_alloc)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers long conn_id = ID_FROM_CHILD_THREAD(my_child_num, my_thread_num);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "new file descriptor %d is too large; you probably need "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "to rebuild Apache with a larger FD_SETSIZE "
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "(currently %d)",
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers current_conn = ap_run_create_connection(p, ap_server_conf, sock,
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt/* requests_this_child has gone to zero or below. See if the admin coded
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers "MaxRequestsPerChild 0", and keep going in that case. Doing it this way
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers simplifies the hot path in worker_thread */
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt /* wow! if you're executing this code, you may have set a record.
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * either this child process has served over 2 billion requests, or
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * you're running a threaded 2.0 on a 16 bit machine.
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * I'll buy pizza and beers at Apachecon for the first person to do
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * the former without cheating (dorking with INT_MAX, or running with
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers * uncommitted performance patches, for example).
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * for the latter case, you probably deserve a beer too. Greg Ames
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt /* XXX If specifying SIG_IGN is guaranteed to unblock a syscall,
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt * then we don't need this goofy function.
ce39bb6909578017aa10031638e724e038f0b859Kay Sieversstatic void *listener_thread(apr_thread_t *thd, void * dummy)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_pool_t *ptrans; /* Pool for per-transaction stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_poll_setup(&pollset, num_listensocks, tpool);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers for(lr = ap_listeners ; lr != NULL ; lr = lr->next)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* Unblock the signal used to wake this thread up, and set a handler for
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(LISTENER_SIGNAL, dummy_signal_handler);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* TODO: Switch to a system where threads reuse the results from earlier
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers poll calls - manoj */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* TODO: requests_this_child should be synchronized - aaron */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_info_wait_for_idler(worker_queue_info,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers break; /* we've been signaled to die now */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "apr_queue_info_wait failed. Attempting to "
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers " shutdown process gracefully.");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We've already decremented the idle worker count inside
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * ap_queue_info_wait_for_idler. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(accept_mutex)))
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (ap_scoreboard_image->parent[process_slot].generation !=
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_scoreboard_image->global->running_generation) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers level = APLOG_DEBUG; /* common to get these at restart time */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "apr_proc_mutex_lock failed. Attempting to shutdown "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "process gracefully.");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers break; /* skip the lock release */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* Only one listener, so skip the poll */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ret = apr_poll(pollset, num_listensocks, &n, -1);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* apr_poll() will only return errors in catastrophic
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * circumstances. Let's try exiting gracefully, for now. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* find a listener */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* XXX: Should we check for POLLERR? */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* create a new transaction pool for each accepted socket */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_allocator_max_free_set(allocator, ap_max_mem_free);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_pool_create_ex(&ptrans, NULL, NULL, allocator);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* later we trash rv and rely on csd to indicate success/failure */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* E[NM]FILE, ENOMEM, etc */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex)))
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (ap_scoreboard_image->parent[process_slot].generation !=
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_scoreboard_image->global->running_generation) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers level = APLOG_DEBUG; /* common to get these at restart time */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "apr_proc_mutex_unlock failed. Attempting to "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "shutdown process gracefully.");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* trash the connection; we couldn't queue the connected
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * socket to a worker
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "ap_queue_push failed");
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex)))
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "apr_proc_mutex_unlock failed. Attempting to "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "shutdown process gracefully.");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_scoreboard_image->parent[process_slot].quiescing = 1;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* wake up the main thread */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* XXX For ungraceful termination/restart, we definitely don't want to
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * wait for active connections to finish but we may want to wait
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * for idle workers to get out of the queue code and release mutexes,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * since those mutexes are cleaned up pretty soon and some systems
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * may not react favorably (i.e., segfault) if operations are attempted
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * on cleaned-up mutexes.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy)
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers apr_pool_t *ptrans; /* Pool for per-transaction stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_info_set_idle(worker_queue_info, last_ptrans);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "ap_queue_info_set_idle failed. Attempting to "
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "shutdown process gracefully.");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_READY, NULL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We get APR_EOF during a graceful shutdown once all the connections
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * accepted by this server process have been handled.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We get APR_EINTR whenever ap_queue_pop() has been interrupted
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * from an explicit call to ap_queue_interrupt_all(). This allows
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * us to unblock threads stuck in ap_queue_pop() when a shutdown
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * is pending.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * If workers_may_exit is set and this is ungraceful termination/
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * restart, we are bound to get an error on some systems (e.g.,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * AIX, which sanity-checks mutex operations) since the queue
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * may have already been cleaned up. Don't log the "error" if
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * workers_may_exit is set.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We got some other error. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "ap_queue_pop failed");
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers requests_this_child--; /* FIXME: should be synchronized - aaron */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_update_child_status_from_indexes(process_slot, thread_slot,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers (dying) ? SERVER_DEAD : SERVER_GRACEFUL, (request_rec *) NULL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void create_listener_thread(thread_starter *ts)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info = (proc_info *)malloc(sizeof(proc_info));
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info->tid = -1; /* listener thread doesn't have a thread slot */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "apr_thread_create: unable to create listener thread");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* In case system resources are maxxed out, we don't want
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Apache running away with the CPU trying to fork over and
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * over and over again if we exit.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * XXX Jeff doesn't see how Apache is going to try to fork again since
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * the exit code is APEXIT_CHILDFATAL
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_os_thread_get(&listener_os_thread, ts->listener);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* XXX under some circumstances not understood, children can get stuck
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * in start_threads forever trying to take over slots which will
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * never be cleaned up; for now there is an APLOG_DEBUG message issued
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * every so often when this condition occurs
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We must create the fd queues before we start up the listener
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * and worker threads. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue));
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers rv = ap_queue_init(worker_queue, ap_threads_per_child, pchild);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers "ap_queue_init() failed");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_info_create(&worker_queue_info, pchild,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "ap_queue_info_create() failed");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_sockets = apr_pcalloc(pchild, ap_threads_per_child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* ap_threads_per_child does not include the listener thread */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers for (i = 0; i < ap_threads_per_child; i++) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int status = ap_scoreboard_image->servers[child_num_arg][i].status;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info = (proc_info *)malloc(sizeof(proc_info));
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "malloc: out of memory");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* We are creating threads right now */
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers ap_update_child_status_from_indexes(my_child_num, i,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers /* We let each thread update its own scoreboard entry. This is
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers * done because it lets us deal with tid better.
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers rv = apr_thread_create(&threads[i], thread_attr,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers "apr_thread_create: unable to create worker thread");
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers /* In case system resources are maxxed out, we don't want
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers Apache running away with the CPU trying to fork over and
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers over and over again if we exit. */
++loops;
return NULL;
if (listener) {
int iter;
iter = 0;
#ifdef HAVE_PTHREAD_KILL
++iter;
for (i = 0; i < ap_threads_per_child; i++) {
pchild));
if (unixd_setup_child()) {
if (ap_max_requests_per_child) {
if (one_process) {
switch(terminate_mode) {
case ST_GRACEFUL:
case ST_UNGRACEFUL:
int pid;
if (one_process) {
set_signals();
if (!pid) {
#ifdef HAVE_BINDPROCESSOR
clean_child_exit(0);
#ifndef MAX_SPAWN_RATE
static int hold_off_on_exponential_spawning;
static void perform_idle_server_maintenance(void)
int idle_thread_count;
int free_length;
int totally_free_length = 0;
int last_non_dead;
int total_non_dead;
free_length = 0;
idle_thread_count = 0;
total_non_dead = 0;
for (i = 0; i < ap_daemons_limit; ++i) {
int any_dying_threads = 0;
int any_dead_threads = 0;
for (j = 0; j < ap_threads_per_child; j++) {
if (all_dead_threads) {
++free_length;
if (!any_dying_threads) {
last_non_dead = i;
if (free_length == 0) {
static int reported = 0;
if (!reported) {
"or Min/MaxSpareThreads), "
for (i = 0; i < free_length; ++i) {
int child_slot;
if (child_slot >= 0) {
for (i = 0; i < ap_threads_per_child; i++)
else if (remaining_children_to_start
else if (is_graceful) {
else if (remaining_children_to_start) {
if (changed_limit_at_restart) {
if (!is_graceful) {
set_signals();
if (!is_graceful) {
if (shutdown_pending) {
if (!child_fatal) {
if (one_process) {
if (is_graceful) {
/* wake up the children...time to die. But we'll have more soon */
pconf = p;
ap_server_conf = s;
return DONE;
if (!one_process) {
return DONE;
return OK;
static int restart_num = 0;
if (!max_clients) {
else if (!max_clients
if (debug) {
no_detach = 0;
is_graceful = 0;
return HTTP_INTERNAL_SERVER_ERROR;
ap_extended_status = 0;
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
return OK;
one_process = 0;
const char *arg)
return err;
return NULL;
const char *arg)
return err;
if (min_spare_threads <= 0) {
return NULL;
const char *arg)
return err;
return NULL;
const char *arg)
int max_clients;
return err;
return NULL;
const char *arg)
return err;
return NULL;
int tmp_server_limit;
return err;
if (first_server_limit &&
return NULL;
return NULL;
int tmp_thread_limit;
return err;
if (first_thread_limit &&
return NULL;
return NULL;
"Maximum number of worker threads per child process for this run of Apache - Upper limit for ThreadsPerChild"),
{ NULL }