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)");
  }