]> granicus.if.org Git - apache/commitdiff
New multi-process multi-threaded MPM for OS/2. Not fully polished but works
authorBrian Havard <bjh@apache.org>
Fri, 17 Aug 2001 17:07:34 +0000 (17:07 +0000)
committerBrian Havard <bjh@apache.org>
Fri, 17 Aug 2001 17:07:34 +0000 (17:07 +0000)
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

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@90289 13f79535-47bb-0310-9956-ffa450edef68

server/mpm/mpmt_os2/Makefile.in [new file with mode: 0644]
server/mpm/mpmt_os2/config5.m4 [new file with mode: 0644]
server/mpm/mpmt_os2/mpm.h [new file with mode: 0644]
server/mpm/mpmt_os2/mpm_default.h [new file with mode: 0644]
server/mpm/mpmt_os2/mpmt_os2.c [new file with mode: 0644]
server/mpm/mpmt_os2/mpmt_os2_child.c [new file with mode: 0644]

diff --git a/server/mpm/mpmt_os2/Makefile.in b/server/mpm/mpmt_os2/Makefile.in
new file mode 100644 (file)
index 0000000..38e598e
--- /dev/null
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME    = libmpmt_os2.la
+LTLIBRARY_SOURCES = mpmt_os2.c mpmt_os2_child.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/mpmt_os2/config5.m4 b/server/mpm/mpmt_os2/config5.m4
new file mode 100644 (file)
index 0000000..b27c296
--- /dev/null
@@ -0,0 +1,5 @@
+if test "$MPM_NAME" = "mpmt_os2" ; then
+    AC_CACHE_SAVE
+    APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+    APR_ADDTO(CFLAGS,-Zmt)
+fi
diff --git a/server/mpm/mpmt_os2/mpm.h b/server/mpm/mpmt_os2/mpm.h
new file mode 100644 (file)
index 0000000..37d2f6c
--- /dev/null
@@ -0,0 +1,74 @@
+/* ====================================================================
+ * 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 */
diff --git a/server/mpm/mpmt_os2/mpm_default.h b/server/mpm/mpmt_os2/mpm_default.h
new file mode 100644 (file)
index 0000000..100beb2
--- /dev/null
@@ -0,0 +1,116 @@
+/* ====================================================================
+ * 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 */
diff --git a/server/mpm/mpmt_os2/mpmt_os2.c b/server/mpm/mpmt_os2/mpmt_os2.c
new file mode 100644 (file)
index 0000000..99806d1
--- /dev/null
@@ -0,0 +1,587 @@
+/* ====================================================================
+ * 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 */
+};
diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c
new file mode 100644 (file)
index 0000000..36a96ca
--- /dev/null
@@ -0,0 +1,463 @@
+/* ====================================================================
+ * 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)");
+}