You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by bj...@apache.org on 2001/08/17 19:07:34 UTC
cvs commit: httpd-2.0/server/mpm/mpmt_os2 Makefile.in config5.m4 mpm.h mpm_default.h mpmt_os2.c mpmt_os2_child.c
bjh 01/08/17 10:07:34
Added: server/mpm/mpmt_os2 Makefile.in config5.m4 mpm.h
mpm_default.h mpmt_os2.c mpmt_os2_child.c
Log:
New multi-process multi-threaded MPM for OS/2. Not fully polished but works
better than spmt_os2 already (graceful restarts actually work). A summary
of the process/thread structure is provided in the comments at the start
of mpmt_os2.c
Revision Changes Path
1.1 httpd-2.0/server/mpm/mpmt_os2/Makefile.in
Index: Makefile.in
===================================================================
LTLIBRARY_NAME = libmpmt_os2.la
LTLIBRARY_SOURCES = mpmt_os2.c mpmt_os2_child.c
include $(top_srcdir)/build/ltlib.mk
1.1 httpd-2.0/server/mpm/mpmt_os2/config5.m4
Index: config5.m4
===================================================================
if test "$MPM_NAME" = "mpmt_os2" ; then
AC_CACHE_SAVE
APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
APR_ADDTO(CFLAGS,-Zmt)
fi
1.1 httpd-2.0/server/mpm/mpmt_os2/mpm.h
Index: mpm.h
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#ifndef APACHE_MPM_MPMT_OS2_H
#define APACHE_MPM_MPMT_OS2_H
#define MPMT_OS2_MPM
#include "httpd.h"
#include "mpm_default.h"
#include "scoreboard.h"
#define MPM_NAME "MPMT_OS2"
extern server_rec *ap_server_conf;
#define AP_MPM_WANT_SET_PIDFILE
#define AP_MPM_WANT_SET_MAX_REQUESTS
#endif /* APACHE_MPM_SPMT_OS2_H */
1.1 httpd-2.0/server/mpm/mpmt_os2/mpm_default.h
Index: mpm_default.h
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#ifndef APACHE_MPM_DEFAULT_H
#define APACHE_MPM_DEFAULT_H
/* Number of servers processes to spawn off by default
*/
#ifndef DEFAULT_START_DAEMON
#define DEFAULT_START_DAEMON 2
#endif
/* We don't need many processes,
* they're only for redundancy in the event of a crash
*/
#define HARD_SERVER_LIMIT 10
/* Limit on the total number of threads per process
*/
#ifndef HARD_THREAD_LIMIT
#define HARD_THREAD_LIMIT 256
#endif
/* Maximum number of *free* server threads --- more than this, and
* they will die off.
*/
#ifndef DEFAULT_MAX_SPARE_THREAD
#define DEFAULT_MAX_SPARE_THREAD 10
#endif
/* Minimum --- fewer than this, and more will be created */
#ifndef DEFAULT_MIN_SPARE_THREAD
#define DEFAULT_MIN_SPARE_THREAD 5
#endif
/* Where the main/parent process's pid is logged */
#ifndef DEFAULT_PIDLOG
#define DEFAULT_PIDLOG "logs/httpd.pid"
#endif
/*
* Interval, in microseconds, between scoreboard maintenance.
*/
#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
#endif
/* Number of requests to try to handle in a single process. If <= 0,
* the children don't die off.
*/
#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
#endif
/* AP_CHILD_THREAD_FROM_ID is used by the scoreboard. */
#define AP_CHILD_THREAD_FROM_ID(i) (i / HARD_THREAD_LIMIT), (i % HARD_THREAD_LIMIT)
#define AP_ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t)
#endif /* AP_MPM_DEFAULT_H */
1.1 httpd-2.0/server/mpm/mpmt_os2/mpmt_os2.c
Index: mpmt_os2.c
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
/* Multi-process, multi-threaded MPM for OS/2
*
* Server consists of
* - a main, parent process
* - a small, static number of child processes
*
* The parent process's job is to manage the child processes. This involves
* spawning children as required to ensure there are always ap_daemons_to_start
* processes accepting connections.
*
* Each child process consists of a a pool of worker threads and a
* main thread that accepts connections & passes them to the workers via
* a work queue. The worker thread pool is dynamic, managed by a maintanence
* thread so that the number of idle threads is kept between
* min_spare_threads & max_spare_threads.
*
*/
/*
Todo list
- Fix log file clashing between child processes
- Enforce MaxClients somehow
- Catch thread exceptions & initiate graceful shutdown of child process
*/
#define CORE_PRIVATE
#define INCL_NOPMAPI
#define INCL_DOS
#define INCL_DOSERRORS
#include "ap_config.h"
#include "httpd.h"
#include "mpm_default.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h"
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
#include "mpm.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "apr_portable.h"
#include "mpm_common.h"
#include "apr_strings.h"
#include <os2.h>
#include <process.h>
server_rec *ap_server_conf;
static apr_pool_t *pconf = NULL; /* Pool for config stuff */
static const char *ap_pid_fname=NULL;
/* Config globals */
static int one_process = 0;
static int ap_daemons_to_start = 0;
static int ap_thread_limit = 0;
static int ap_max_requests_per_child = 0;
int ap_min_spare_threads = 0;
int ap_max_spare_threads = 0;
/* Keep track of a few interesting statistics */
int ap_max_daemons_limit = -1;
/* volatile just in case */
static int volatile shutdown_pending;
static int volatile restart_pending;
static int volatile is_graceful = 0;
ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */
static int is_parent_process=TRUE;
HMTX ap_mpm_accept_mutex = 0;
/* An array of these is stored in a shared memory area for passing
* sockets from the parent to child processes
*/
typedef struct {
struct sockaddr_in name;
apr_os_sock_t listen_fd;
} listen_socket_t;
typedef struct {
apr_os_file_t errorlog_fd;
apr_time_t restart_time;
HMTX accept_mutex;
listen_socket_t listeners[1];
} parent_info_t;
static char master_main();
static void spawn_child(int slot);
void ap_mpm_child_main(apr_pool_t *pconf);
static void set_signals();
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
{
char *listener_shm_name;
parent_info_t *parent_info;
ULONG rc;
pconf = _pconf;
ap_server_conf = s;
restart_pending = 0;
DosSetMaxFH(ap_thread_limit * 2);
listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid());
rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ);
is_parent_process = rc != 0;
ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid());
if (rc == 0) {
/* Child process */
ap_listen_rec *lr;
int num_listeners = 0;
apr_file_close(ap_server_conf->error_log);
apr_os_file_put(&ap_server_conf->error_log, &parent_info->errorlog_fd, pconf);
ap_restart_time = parent_info->restart_time;
ap_mpm_accept_mutex = parent_info->accept_mutex;
/* Set up a default listener if necessary */
if (ap_listeners == NULL) {
ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
ap_listeners = lr;
apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC,
DEFAULT_HTTP_PORT, 0, s->process->pool);
apr_socket_create(&lr->sd, lr->bind_addr->sa.sin.sin_family,
SOCK_STREAM, s->process->pool);
}
for (lr = ap_listeners; lr; lr = lr->next) {
apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf);
num_listeners++;
}
DosFreeMem(parent_info);
/* Do the work */
ap_mpm_child_main(pconf);
/* Outta here */
return 1;
}
else {
/* Parent process */
char restart;
is_parent_process = TRUE;
ap_log_pid(pconf, ap_pid_fname);
if (ap_setup_listeners(ap_server_conf) < 1) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
"no listening sockets available, shutting down");
return 1;
}
restart = master_main();
++ap_my_generation;
ap_scoreboard_image->global.running_generation = ap_my_generation;
if (!restart) {
const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
if (pidfile != NULL && remove(pidfile) == 0) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS,
ap_server_conf, "removed PID file %s (pid=%d)",
pidfile, getpid());
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
"caught SIGTERM, shutting down");
return 1;
}
} /* Parent process */
return 0; /* Restart */
}
/* Main processing of the parent process
* returns TRUE if restarting
*/
static char master_main()
{
server_rec *s = ap_server_conf;
ap_listen_rec *lr;
parent_info_t *parent_info;
char *listener_shm_name;
int listener_num, num_listeners, slot;
ULONG rc;
printf("%s \n", ap_get_server_version());
set_signals();
if (ap_setup_listeners(ap_server_conf) < 1) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
"no listening sockets available, shutting down");
return FALSE;
}
/* Allocate a shared memory block for the array of listeners */
for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
num_listeners++;
}
listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid());
rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name,
sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t),
PAG_READ|PAG_WRITE|PAG_COMMIT);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
"failure allocating shared memory, shutting down");
return FALSE;
}
/* Store the listener sockets in the shared memory area for our children to see */
for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) {
apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd);
}
/* Create mutex to prevent multiple child processes from detecting
* a connection with apr_poll()
*/
rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
"failure creating accept mutex, shutting down");
return FALSE;
}
ap_restart_time = apr_time_now();
parent_info->restart_time = ap_restart_time;
parent_info->accept_mutex = ap_mpm_accept_mutex;
apr_os_file_get(&parent_info->errorlog_fd, s->error_log);
/* Allocate shared memory for scoreboard */
if (ap_scoreboard_image == NULL) {
rc = DosAllocSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname,
sizeof(scoreboard), PAG_COMMIT|PAG_READ|PAG_WRITE);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"unable to allocate shared memory for scoreboard , exiting");
return FALSE;
}
memset(ap_scoreboard_image, 0, sizeof(scoreboard));
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
"%s configured -- resuming normal operations",
ap_get_server_version());
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
"Server built: %s", ap_get_server_built());
if (one_process) {
ap_scoreboard_image->parent[0].pid = getpid();
ap_mpm_child_main(pconf);
return FALSE;
}
while (!restart_pending && !shutdown_pending) {
RESULTCODES proc_rc;
PID child_pid;
int active_children = 0;
/* Count number of active children */
for (slot=0; slot < HARD_SERVER_LIMIT; slot++) {
active_children += ap_scoreboard_image->parent[slot].pid != 0 &&
!ap_scoreboard_image->parent[slot].quiescing;
}
/* Spawn children if needed */
for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) {
if (ap_scoreboard_image->parent[slot].pid == 0) {
spawn_child(slot);
active_children++;
}
}
rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0);
if (rc == 0) {
/* A child has terminated, remove its scoreboard entry & terminate if necessary */
for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++);
if (slot < HARD_SERVER_LIMIT) {
ap_scoreboard_image->parent[slot].pid = 0;
ap_scoreboard_image->parent[slot].quiescing = 0;
if (proc_rc.codeTerminate == TC_EXIT) {
/* Child terminated normally, check its exit code and
* terminate server if child indicates a fatal error
*/
if (proc_rc.codeResult == APEXIT_CHILDFATAL)
break;
}
}
} else if (rc == ERROR_CHILD_NOT_COMPLETE) {
/* No child exited, lets sleep for a while.... */
apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
}
}
/* Signal children to shut down, either gracefully or immediately */
for (slot=0; slot<HARD_SERVER_LIMIT; slot++) {
kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM);
}
DosFreeMem(parent_info);
return restart_pending;
}
static void spawn_child(int slot)
{
PPIB ppib;
PTIB ptib;
char fail_module[100];
char progname[CCHMAXPATH];
RESULTCODES proc_rc;
ULONG rc;
ap_scoreboard_image->parent[slot].generation = ap_my_generation;
DosGetInfoBlocks(&ptib, &ppib);
DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname);
rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT,
ppib->pib_pchcmd, NULL, &proc_rc, progname);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"error spawning child, slot %d", slot);
}
if (ap_max_daemons_limit < slot) {
ap_max_daemons_limit = slot;
}
ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
}
/* Signal handling routines */
static void sig_term(int sig)
{
shutdown_pending = 1;
}
static void sig_restart(int sig)
{
if (sig == SIGUSR1) {
is_graceful = 1;
}
restart_pending = 1;
}
static void set_signals()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
if (sigaction(SIGINT, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
sa.sa_handler = sig_restart;
if (sigaction(SIGHUP, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
if (sigaction(SIGUSR1, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
}
/* Enquiry functions used get MPM status info */
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
{
switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
*result = ap_max_daemons_limit;
return APR_SUCCESS;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_DYNAMIC;
return APR_SUCCESS;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_NOT_SUPPORTED;
return APR_SUCCESS;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = HARD_SERVER_LIMIT;
return APR_SUCCESS;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = HARD_THREAD_LIMIT;
return APR_SUCCESS;
case AP_MPMQ_MIN_SPARE_DEAMONS:
*result = 0;
return APR_SUCCESS;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
return APR_SUCCESS;
case AP_MPMQ_MAX_REQUESTS_DEAMON:
*result = ap_max_requests_per_child;
return APR_SUCCESS;
}
return APR_ENOTIMPL;
}
int ap_graceful_stop_signalled(void)
{
return is_graceful;
}
/* Configuration handling stuff */
static void mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
{
one_process = !!ap_exists_config_define("ONE_PROCESS");
is_graceful = 0;
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
ap_thread_limit = HARD_THREAD_LIMIT;
ap_pid_fname = DEFAULT_PIDLOG;
ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_extended_status = 0;
ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
}
static void mpmt_os2_hooks(apr_pool_t *p)
{
ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
}
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_daemons_to_start = atoi(arg);
return NULL;
}
static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_min_spare_threads = atoi(arg);
if (ap_min_spare_threads <= 0) {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"WARNING: detected MinSpareThreads set to non-positive.");
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"Resetting to 1 to avoid almost certain Apache failure.");
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"Please read the documentation.");
ap_min_spare_threads = 1;
}
return NULL;
}
static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_max_spare_threads = atoi(arg);
return NULL;
}
static const command_rec mpmt_os2_cmds[] = {
LISTEN_COMMANDS
AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF,
"Number of child processes launched at server startup" ),
AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
"Minimum number of idle children, to handle request spikes"),
AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
"Maximum number of idle children"),
{ NULL }
};
module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
MPM20_MODULE_STUFF,
NULL, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
mpmt_os2_cmds, /* command apr_table_t */
mpmt_os2_hooks, /* register_hooks */
};
1.1 httpd-2.0/server/mpm/mpmt_os2/mpmt_os2_child.c
Index: mpmt_os2_child.c
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#define CORE_PRIVATE
#define INCL_NOPMAPI
#define INCL_DOS
#define INCL_DOSERRORS
#include "ap_config.h"
#include "httpd.h"
#include "mpm_default.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h"
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
#include "mpm.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "apr_portable.h"
#include "mpm_common.h"
#include "apr_strings.h"
#include <os2.h>
#include <process.h>
typedef struct {
apr_pool_t *pconn;
apr_socket_t *conn_sd;
} worker_args_t;
#define WORKTYPE_CONN 0
#define WORKTYPE_EXIT 1
static apr_pool_t *pchild = NULL;
static int child_slot;
static int shutdown_pending = 0;
extern int ap_my_generation;
static int volatile is_graceful = 1;
HEV shutdown_event; /* signaled when this child is shutting down */
/* grab some MPM globals */
extern int ap_min_spare_threads;
extern int ap_max_spare_threads;
extern HMTX ap_mpm_accept_mutex;
static void worker_main(void *vpArg);
static void clean_child_exit(int code);
static void set_signals();
static void server_maintenance(void *vpArg);
static void clean_child_exit(int code)
{
if (pchild) {
apr_pool_destroy(pchild);
}
exit(code);
}
void ap_mpm_child_main(apr_pool_t *pconf)
{
ap_listen_rec *lr = NULL;
ap_listen_rec *first_lr = NULL;
int requests_this_child = 0;
apr_socket_t *sd = ap_listeners->sd;
int nsds, rv = 0;
unsigned long ulTimes;
int my_pid = getpid();
ULONG rc, c;
HQUEUE workq;
apr_pollfd_t *pollset;
int num_listeners;
TID server_maint_tid;
/* Stop Ctrl-C/Ctrl-Break signals going to child processes */
DosSetSignalExceptionFocus(0, &ulTimes);
set_signals();
/* Create pool for child */
apr_pool_create(&pchild, pconf);
/* Create an event semaphore used to trigger other threads to shutdown */
rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"unable to create shutdown semaphore, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
/* Gain access to the scoreboard. */
rc = DosGetNamedSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname,
PAG_READ|PAG_WRITE);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"scoreboard not readable in child, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
/* Gain access to the accpet mutex */
rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"accept mutex couldn't be accessed in child, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
/* Find our pid in the scoreboard so we know what slot our parent allocated us */
for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
if (child_slot == HARD_SERVER_LIMIT) {
ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, ap_server_conf,
"child pid not found in scoreboard, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
/* Set up an OS/2 queue for passing connections & termination requests
* to worker threads
*/
rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"unable to create work queue, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
/* Create initial pool of worker threads */
for (c = 0; c < ap_min_spare_threads; c++) {
// ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
}
/* Start maintenance thread */
server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
/* Set up poll */
for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
num_listeners++;
}
apr_poll_setup(&pollset, num_listeners, pchild);
for (lr = ap_listeners; lr; lr = lr->next) {
apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
}
/* Main connection accept loop */
do {
apr_pool_t *pconn;
worker_args_t *worker_args;
apr_pool_create(&pconn, pchild);
worker_args = apr_palloc(pconn, sizeof(worker_args_t));
worker_args->pconn = pconn;
if (num_listeners == 1) {
rv = apr_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
} else {
rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
if (shutdown_pending) {
DosReleaseMutexSem(ap_mpm_accept_mutex);
break;
}
rv = APR_FROM_OS_ERROR(rc);
if (rv == APR_SUCCESS) {
rv = apr_poll(pollset, &nsds, -1);
DosReleaseMutexSem(ap_mpm_accept_mutex);
}
if (rv == APR_SUCCESS) {
if (first_lr == NULL) {
first_lr = ap_listeners;
}
lr = first_lr;
do {
apr_int16_t event;
apr_poll_revents_get(&event, lr->sd, pollset);
if (event == APR_POLLIN) {
apr_sockaddr_t *sa;
apr_port_t port;
apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
apr_sockaddr_port_get(&port, sa);
first_lr = lr->next;
break;
}
lr = lr->next;
if (!lr) {
lr = ap_listeners;
}
} while (lr != first_lr);
if (lr == first_lr) {
continue;
}
sd = lr->sd;
rv = apr_accept(&worker_args->conn_sd, sd, pconn);
}
}
if (rv != APR_SUCCESS) {
if (!APR_STATUS_IS_EINTR(rv)) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
"apr_accept");
clean_child_exit(APEXIT_CHILDFATAL);
}
} else {
DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
requests_this_child++;
}
if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
break;
} while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global.running_generation);
ap_scoreboard_image->parent[child_slot].quiescing = 1;
DosPostEventSem(shutdown_event);
DosWaitThread(&server_maint_tid, DCWW_WAIT);
if (is_graceful) {
char someleft;
/* tell our worker threads to exit */
for (c=0; c<HARD_THREAD_LIMIT; c++) {
if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
}
}
do {
someleft = 0;
for (c=0; c<HARD_THREAD_LIMIT; c++) {
if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
someleft = 1;
DosSleep(1000);
break;
}
}
} while (someleft);
} else {
DosPurgeQueue(workq);
for (c=0; c<HARD_THREAD_LIMIT; c++) {
if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
}
}
}
apr_pool_destroy(pchild);
}
void add_worker()
{
int thread_slot;
/* Find a free thread slot */
for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
ap_scoreboard_image->servers[child_slot][thread_slot].tid =
_beginthread(worker_main, NULL, 128*1024, (void *)thread_slot);
break;
}
}
}
static void worker_main(void *vpArg)
{
long conn_id;
conn_rec *current_conn;
apr_pool_t *pconn;
worker_args_t *worker_args;
HQUEUE workq;
PID owner;
int rc;
REQUESTDATA rd;
ULONG len;
BYTE priority;
int thread_slot = (int)vpArg;
rc = DosOpenQueue(&owner, &workq,
apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"unable to open work queue, exiting");
ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
}
conn_id = AP_ID_FROM_CHILD_THREAD(child_slot, thread_slot);
ap_update_child_status(child_slot, thread_slot, SERVER_READY, NULL);
while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
rc == 0 && rd.ulData != WORKTYPE_EXIT) {
pconn = worker_args->pconn;
ap_sock_disable_nagle(worker_args->conn_sd);
current_conn = ap_new_connection(pconn, ap_server_conf, worker_args->conn_sd, conn_id);
if (current_conn) {
ap_process_connection(current_conn);
ap_lingering_close(current_conn);
}
apr_pool_destroy(pconn);
ap_update_child_status(child_slot, thread_slot, SERVER_READY, NULL);
}
ap_update_child_status(child_slot, thread_slot, SERVER_DEAD, NULL);
}
static void server_maintenance(void *vpArg)
{
int num_idle, num_needed;
ULONG num_pending = 0;
int threadnum;
HQUEUE workq;
ULONG rc;
PID owner;
rc = DosOpenQueue(&owner, &workq,
apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"unable to open work queue in maintenance thread");
return;
}
do {
for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
}
DosQueryQueue(workq, &num_pending);
num_needed = ap_min_spare_threads - num_idle + num_pending;
if (num_needed > 0) {
for (threadnum=0; threadnum < num_needed; threadnum++) {
add_worker();
}
}
if (num_idle - num_pending > ap_max_spare_threads) {
DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
}
} while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
}
/* Signal handling routines */
static void sig_term(int sig)
{
shutdown_pending = 1;
is_graceful = 0;
signal(SIGTERM, SIG_DFL);
}
static void sig_hup(int sig)
{
shutdown_pending = 1;
is_graceful = 1;
}
static void set_signals()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
sa.sa_handler = sig_hup;
if (sigaction(SIGHUP, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
}