You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2002/07/29 07:12:50 UTC

cvs commit: httpd-2.0/server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h

wrowe       2002/07/28 22:12:50

  Modified:    server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h
  Log:
    pconf global factors out nicely.  The one other pconf appears to be
    eqivilant to pchild.
  
  Revision  Changes    Path
  1.2       +3 -4      httpd-2.0/server/mpm/winnt/child.c
  
  Index: child.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/child.c,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- child.c	29 Jul 2002 05:06:20 -0000	1.1
  +++ child.c	29 Jul 2002 05:12:50 -0000	1.2
  @@ -83,7 +83,6 @@
   
   /* shared with mpm_winnt.c */
   extern DWORD my_pid;
  -extern apr_pool_t *pconf;
   
   /* used by parent to signal the child to start and exit */
   /* shared with mpm_winnt.c, but should be private to child.c */
  @@ -97,7 +96,7 @@
   /* Queue for managing the passing of COMP_CONTEXTs between
    * the accept and worker threads.
    */
  -static apr_pool_t *pchild = NULL;
  +static apr_pool_t *pchild;
   static int shutdown_in_progress = 0;
   static int workers_may_exit = 0;
   static unsigned int g_blocked_threads = 0;
  @@ -417,7 +416,7 @@
   
       if (context == NULL) {
           /* allocate the completion context and the transaction pool */
  -        context = apr_pcalloc(pconf, sizeof(COMP_CONTEXT));
  +        context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
           apr_pool_create(&context->ptrans, pchild);
           apr_pool_tag(context->ptrans, "ptrans");
           context->ba = apr_bucket_alloc_create(pchild);
  @@ -757,7 +756,7 @@
   }
   
   
  -void child_main()
  +void child_main(apr_pool_t *pconf)
   {
       apr_status_t status;
       apr_hash_t *ht;
  
  
  
  1.290     +103 -1124 httpd-2.0/server/mpm/winnt/mpm_winnt.c
  
  Index: mpm_winnt.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.c,v
  retrieving revision 1.289
  retrieving revision 1.290
  diff -u -r1.289 -r1.290
  --- mpm_winnt.c	27 Jul 2002 18:13:59 -0000	1.289
  +++ mpm_winnt.c	29 Jul 2002 05:12:50 -0000	1.290
  @@ -56,6 +56,8 @@
    * University of Illinois, Urbana-Champaign.
    */
   
  +#ifdef WIN32
  +
   #define CORE_PRIVATE 
   #include "httpd.h" 
   #include "http_main.h" 
  @@ -108,19 +110,10 @@
    */
   extern apr_shm_t *ap_scoreboard_shm;
   server_rec *ap_server_conf;
  -typedef HANDLE thread;
   
   /* Definitions of WINNT MPM specific config globals */
  -apr_pool_t *pconf;
  -static apr_pool_t *pchild = NULL;
  -static int workers_may_exit = 0;
  -static int shutdown_in_progress = 0;
  -static unsigned int g_blocked_threads = 0;
  -
   static HANDLE shutdown_event;	/* used to signal the parent to shutdown */
   static HANDLE restart_event;	/* used to signal the parent to restart */
  -static HANDLE exit_event;       /* used by parent to signal the child to exit */
  -static HANDLE max_requests_per_child_event;
   
   static char ap_coredump_dir[MAX_STRING_LEN];
   
  @@ -129,23 +122,31 @@
   
   OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
   
  -apr_proc_mutex_t *start_mutex;
  -static DWORD my_pid;
   static DWORD parent_pid;
  +DWORD my_pid;
   
   int ap_threads_per_child = 0;
   
   /* ap_my_generation are used by the scoreboard code */
   ap_generation_t volatile ap_my_generation=0;
   
  -/* Queue for managing the passing of COMP_CONTEXTs between
  - * the accept and worker threads.
  +
  +/* shared by service.c as global, although 
  + * perhaps it should be private.
  + */
  +apr_pool_t *pconf;
  +
  +
  +/* definitions from child.c */
  +void child_main(apr_pool_t *pconf);
  +
  +/* used by parent to signal the child to start and exit
  + * NOTE: these are not sophisticated enough for multiple children
  + * so they ultimately should not be shared with child.c
    */
  -static apr_thread_mutex_t  *qlock;
  -static PCOMP_CONTEXT qhead = NULL;
  -static PCOMP_CONTEXT qtail = NULL;
  -static int num_completion_contexts = 0;
  -static HANDLE ThreadDispatchIOCP = NULL;
  +extern apr_proc_mutex_t *start_mutex;
  +extern HANDLE exit_event;  
  +
   
   /* Stub functions until this MPM supports the connection status API */
   
  @@ -205,206 +206,6 @@
   };
   
   
  -AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context)
  -{
  -    /* Recycle the completion context.
  -     * - clear the ptrans pool
  -     * - put the context on the queue to be consumed by the accept thread
  -     * Note: 
  -     * context->accept_socket may be in a disconnected but reusable 
  -     * state so -don't- close it.
  -     */
  -    if (context) {
  -        apr_pool_clear(context->ptrans);
  -        context->next = NULL;
  -        ResetEvent(context->Overlapped.hEvent);
  -        apr_thread_mutex_lock(qlock);
  -        if (qtail)
  -            qtail->next = context;
  -        else
  -            qhead = context;
  -        qtail = context;
  -        apr_thread_mutex_unlock(qlock);
  -    }
  -}
  -
  -AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void)
  -{
  -    apr_status_t rv;
  -    PCOMP_CONTEXT context = NULL;
  -
  -    /* Grab a context off the queue */
  -    apr_thread_mutex_lock(qlock);
  -    if (qhead) {
  -        context = qhead;
  -        qhead = qhead->next;
  -        if (!qhead)
  -            qtail = NULL;
  -    }
  -    apr_thread_mutex_unlock(qlock);
  -
  -    /* If we failed to grab a context off the queue, alloc one out of 
  -     * the child pool. There may be up to ap_threads_per_child contexts
  -     * in the system at once.
  -     */
  -    if (!context) {
  -        if (num_completion_contexts >= ap_threads_per_child) {
  -            static int reported = 0;
  -            if (!reported) {
  -                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
  -                             "Server ran out of threads to serve requests. Consider "
  -                             "raising the ThreadsPerChild setting");
  -                reported = 1;
  -            }
  -            return NULL;
  -        }
  -        /* Note:
  -         * Multiple failures in the next two steps will cause the pchild pool
  -         * to 'leak' storage. I don't think this is worth fixing...
  -         */
  -        context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
  -
  -        context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  -        if (context->Overlapped.hEvent == NULL) {
  -            /* Hopefully this is a temporary condition ... */
  -            ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
  -                         "mpm_get_completion_context: CreateEvent failed.");
  -            return NULL;
  -        }
  -
  -        /* Create the tranaction pool */
  -        if ((rv = apr_pool_create(&context->ptrans, pchild)) != APR_SUCCESS) {
  -            ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
  -                         "mpm_get_completion_context: Failed to create the transaction pool.");
  -            CloseHandle(context->Overlapped.hEvent);
  -            return NULL;
  -        }
  -        apr_pool_tag(context->ptrans, "ptrans");
  -
  -        context->accept_socket = INVALID_SOCKET;
  -        context->ba = apr_bucket_alloc_create(pchild);
  -        apr_atomic_inc(&num_completion_contexts);
  -    }
  -
  -    return context;
  -}
  -
  -AP_DECLARE(apr_status_t) mpm_post_completion_context(PCOMP_CONTEXT context, 
  -                                                     io_state_e state)
  -{
  -    LPOVERLAPPED pOverlapped;
  -    if (context)
  -        pOverlapped = &context->Overlapped;
  -    else
  -        pOverlapped = NULL;
  -
  -    PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped);
  -    return APR_SUCCESS;
  -}
  -
  -/* This is the helper code to resolve late bound entry points 
  - * missing from one or more releases of the Win32 API...
  - * but it sure would be nice if we didn't duplicate this code
  - * from the APR ;-)
  - */
  -static const char* const lateDllName[DLL_defined] = {
  -    "kernel32", "advapi32", "mswsock",  "ws2_32"  };
  -static HMODULE lateDllHandle[DLL_defined] = {
  -    NULL,       NULL,       NULL,       NULL      };
  -
  -FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char* fnName, int ordinal)
  -{
  -    if (!lateDllHandle[fnLib]) { 
  -        lateDllHandle[fnLib] = LoadLibrary(lateDllName[fnLib]);
  -        if (!lateDllHandle[fnLib])
  -            return NULL;
  -    }
  -    if (ordinal)
  -        return GetProcAddress(lateDllHandle[fnLib], (char *) ordinal);
  -    else
  -        return GetProcAddress(lateDllHandle[fnLib], fnName);
  -}
  -
  -/* To share the semaphores with other processes, we need a NULL ACL
  - * Code from MS KB Q106387
  - */
  -static PSECURITY_ATTRIBUTES GetNullACL()
  -{
  -    PSECURITY_DESCRIPTOR pSD;
  -    PSECURITY_ATTRIBUTES sa;
  -
  -    sa  = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
  -    sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES));
  -
  -    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
  -    sa->lpSecurityDescriptor = pSD;
  -
  -    if (pSD == NULL || sa == NULL) {
  -        return NULL;
  -    }
  -    apr_set_os_error(0);
  -    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
  -	|| apr_get_os_error()) {
  -        LocalFree( pSD );
  -        LocalFree( sa );
  -        return NULL;
  -    }
  -    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
  -	|| apr_get_os_error()) {
  -        LocalFree( pSD );
  -        LocalFree( sa );
  -        return NULL;
  -    }
  -
  -    sa->bInheritHandle = TRUE;
  -    return sa;
  -}
  -
  -static void CleanNullACL( void *sa ) {
  -    if( sa ) {
  -        LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
  -        LocalFree( sa );
  -    }
  -}
  -
  -/*
  - * The Win32 call WaitForMultipleObjects will only allow you to wait for 
  - * a maximum of MAXIMUM_WAIT_OBJECTS (current 64).  Since the threading 
  - * model in the multithreaded version of apache wants to use this call, 
  - * we are restricted to a maximum of 64 threads.  This is a simplistic 
  - * routine that will increase this size.
  - */
  -static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, 
  -                                   DWORD dwSeconds)
  -{
  -    time_t tStopTime;
  -    DWORD dwRet = WAIT_TIMEOUT;
  -    DWORD dwIndex=0;
  -    BOOL bFirst = TRUE;
  -  
  -    tStopTime = time(NULL) + dwSeconds;
  -  
  -    do {
  -        if (!bFirst)
  -            Sleep(1000);
  -        else
  -            bFirst = FALSE;
  -          
  -        for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
  -            dwRet = WaitForMultipleObjects( 
  -                min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
  -                lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 
  -                0, 0);
  -                                           
  -            if (dwRet != WAIT_TIMEOUT) {                                          
  -              break;
  -            }
  -        }
  -    } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
  -    
  -    return dwRet;
  -}
  -
   /*
    * Signalling Apache on NT.
    *
  @@ -450,7 +251,8 @@
   	"%s_restart", signal_name_prefix);    
   }
   
  -static int volatile is_graceful = 0;
  +int volatile is_graceful = 0;
  +
   AP_DECLARE(int) ap_graceful_stop_signalled(void)
   {
       return is_graceful;
  @@ -517,81 +319,6 @@
       CloseHandle(e);
   }
   
  -/* set_listeners_noninheritable()
  - * Make the listening socket handles noninheritable by processes
  - * started out of this process.
  - */
  -static int set_listeners_noninheritable(apr_pool_t *p) 
  -{
  -    ap_listen_rec *lr;
  -    HANDLE dup;
  -    SOCKET nsd;
  -    HANDLE hProcess = GetCurrentProcess();
  -
  -    for (lr = ap_listeners; lr; lr = lr->next) {
  -        apr_os_sock_get(&nsd,lr->sd);
  -        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
  -            if (!DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup, 
  -                                 0, FALSE, DUPLICATE_SAME_ACCESS)) {
  -                ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
  -                             "set_listeners_noninheritable: DuplicateHandle failed.");
  -            }
  -            else {
  -                closesocket(nsd);
  -                nsd = (SOCKET) dup;
  -                apr_os_sock_put(&lr->sd, &nsd, p);
  -            }
  -        }
  -        else {
  -            /* A different approach.  Many users report errors such as 
  -             * (32538)An operation was attempted on something that is not 
  -             * a socket.  : Parent: WSADuplicateSocket failed...
  -             *
  -             * This appears that the duplicated handle is no longer recognized
  -             * as a socket handle.  SetHandleInformation should overcome that
  -             * problem by not altering the handle identifier.  But this won't
  -             * work on 9x - it's unsupported.
  -             */
  -            if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
  -                ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
  -                             "set_listeners_noninheritable: SetHandleInformation failed.");
  -            }
  -        }
  -    }
  -
  -    if (my_pid == parent_pid) {
  -        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
  -                     "Parent: Marked listeners as not inheritable.");
  -    } else {
  -        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
  -                     "Child %d: Marked listeners as not inheritable.", my_pid);
  -    }
  -    return 1;
  -}
  -
  -/*
  - * find_ready_listener()
  - * Only used by Win9* and should go away when the win9*_accept() function is 
  - * reimplemented using apr_poll().
  - */
  -static ap_listen_rec *head_listener;
  -static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
  -{
  -    ap_listen_rec *lr;
  -    SOCKET nsd;
  -
  -    for (lr = head_listener; lr ; lr = lr->next) {
  -        apr_os_sock_get(&nsd, lr->sd);
  -	if (FD_ISSET(nsd, main_fds)) {
  -	    head_listener = lr->next;
  -            if (head_listener == NULL)
  -                head_listener = ap_listeners;
  -
  -	    return (lr);
  -	}
  -    }
  -    return NULL;
  -}
   
   /*
    * Passed the following handles [in sync with send_handles_to_child()]
  @@ -600,7 +327,7 @@
    *   exit event  [save to poll later]
    *   scoreboard shm handle [to recreate the ap_scoreboard]
    */
  -void get_handles_from_parent(server_rec *s)
  +void get_handles_from_parent(server_rec *s, apr_shm_t *scoreboard_shm)
   {
       HANDLE pipe;
       HANDLE hScore;
  @@ -659,829 +386,6 @@
                    "Child %d: Retrieved our scoreboard from the parent.", my_pid);
   }
   
  -/* 
  - * get_listeners_from_parent()
  - * The listen sockets are opened in the parent. This function, which runs
  - * exclusively in the child process, receives them from the parent and
  - * makes them availeble in the child.
  - */
  -void get_listeners_from_parent(server_rec *s)
  -{
  -    WSAPROTOCOL_INFO WSAProtocolInfo;
  -    HANDLE pipe;
  -    ap_listen_rec *lr;
  -    DWORD BytesRead;
  -    int lcnt = 0;
  -    SOCKET nsd;
  -
  -    /* Set up a default listener if necessary */
  -    if (ap_listeners == NULL) {
  -        ap_listen_rec *lr;
  -        lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
  -        lr->sd = NULL;
  -        lr->next = ap_listeners;
  -        ap_listeners = lr;
  -    }
  -
  -    /* Open the pipe to the parent process to receive the inherited socket
  -     * data. The sockets have been set to listening in the parent process.
  -     */
  -    pipe = GetStdHandle(STD_INPUT_HANDLE);
  -
  -    for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
  -        if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), 
  -                      &BytesRead, (LPOVERLAPPED) NULL)) {
  -            ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
  -                         "setup_inherited_listeners: Unable to read socket data from parent");
  -            exit(APEXIT_CHILDINIT);
  -        }
  -        nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
  -                        &WSAProtocolInfo, 0, 0);
  -        if (nsd == INVALID_SOCKET) {
  -            ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
  -                         "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
  -            exit(APEXIT_CHILDINIT);
  -        }
  -        apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
  -    }
  -
  -    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
  -                 "Child %d: retrieved %d listeners from parent", my_pid, lcnt);
  -
  -    if (!set_listeners_noninheritable(s->process->pool)) {
  -        exit(APEXIT_CHILDINIT);
  -    }
  -}
  -
  -
  -/* Windows 9x specific code...
  - * Accept processing for on Windows 95/98 uses a producer/consumer queue 
  - * model. A single thread accepts connections and queues the accepted socket 
  - * to the accept queue for consumption by a pool of worker threads.
  - *
  - * win9x_accept()
  - *    The accept threads runs this function, which accepts connections off 
  - *    the network and calls add_job() to queue jobs to the accept_queue.
  - * add_job()/remove_job()
  - *    Add or remove an accepted socket from the list of sockets 
  - *    connected to clients. allowed_globals.jobmutex protects
  - *    against multiple concurrent access to the linked list of jobs.
  - * win9x_get_connection()
  - *    Calls remove_job() to pull a job from the accept queue. All the worker 
  - *    threads block on remove_job.
  - */
  -
  -typedef struct joblist_s {
  -    struct joblist_s *next;
  -    int sock;
  -} joblist;
  -
  -typedef struct globals_s {
  -    HANDLE jobsemaphore;
  -    joblist *jobhead;
  -    joblist *jobtail;
  -    apr_thread_mutex_t *jobmutex;
  -    int jobcount;
  -} globals;
  -
  -globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
  -#define MAX_SELECT_ERRORS 100
  -
  -static void add_job(int sock)
  -{
  -    joblist *new_job;
  -
  -    new_job = (joblist *) malloc(sizeof(joblist));
  -    if (new_job == NULL) {
  -	ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
  -                     "Ouch!  Out of memory in add_job()!");
  -        return;
  -    }
  -    new_job->next = NULL;
  -    new_job->sock = sock;
  -
  -    apr_thread_mutex_lock(allowed_globals.jobmutex);
  -
  -    if (allowed_globals.jobtail != NULL)
  -	allowed_globals.jobtail->next = new_job;
  -    allowed_globals.jobtail = new_job;
  -    if (!allowed_globals.jobhead)
  -	allowed_globals.jobhead = new_job;
  -    allowed_globals.jobcount++;
  -    ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL);
  -
  -    apr_thread_mutex_unlock(allowed_globals.jobmutex);
  -}
  -
  -static int remove_job(void)
  -{
  -    joblist *job;
  -    int sock;
  -
  -    WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
  -    apr_thread_mutex_lock(allowed_globals.jobmutex);
  -
  -    if (shutdown_in_progress && !allowed_globals.jobhead) {
  -        apr_thread_mutex_unlock(allowed_globals.jobmutex);
  -	return (-1);
  -    }
  -    job = allowed_globals.jobhead;
  -    ap_assert(job);
  -    allowed_globals.jobhead = job->next;
  -    if (allowed_globals.jobhead == NULL)
  -	allowed_globals.jobtail = NULL;
  -    apr_thread_mutex_unlock(allowed_globals.jobmutex);
  -    sock = job->sock;
  -    free(job);
  -
  -    return (sock);
  -}
  -
  -static void win9x_accept(void * dummy)
  -{
  -    struct timeval tv;
  -    fd_set main_fds;
  -    int wait_time = 1;
  -    int csd;
  -    SOCKET nsd = INVALID_SOCKET;
  -    struct sockaddr_in sa_client;
  -    int count_select_errors = 0;
  -    int rc;
  -    int clen;
  -    ap_listen_rec *lr;
  -    struct fd_set listenfds;
  -    SOCKET listenmaxfd = INVALID_SOCKET;
  -
  -    /* Setup the listeners 
  -     * ToDo: Use apr_poll()
  -     */
  -    FD_ZERO(&listenfds);
  -    for (lr = ap_listeners; lr; lr = lr->next) {
  -        if (lr->sd != NULL) {
  -            apr_os_sock_get(&nsd, lr->sd);
  -            FD_SET(nsd, &listenfds);
  -            if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) {
  -                listenmaxfd = nsd;
  -            }
  -        }
  -    }
  -    head_listener = ap_listeners;
  -
  -    while (!shutdown_in_progress) {
  -	tv.tv_sec = wait_time;
  -	tv.tv_usec = 0;
  -	memcpy(&main_fds, &listenfds, sizeof(fd_set));
  -
  -	rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
  -
  -        if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
  -            count_select_errors = 0;    /* reset count of errors */            
  -            continue;
  -        }
  -        else if (rc == SOCKET_ERROR) {
  -            /* A "real" error occurred, log it and increment the count of
  -             * select errors. This count is used to ensure we don't go into
  -             * a busy loop of continuous errors.
  -             */
  -            ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf, 
  -                         "select failed with error %d", apr_get_netos_error());
  -            count_select_errors++;
  -            if (count_select_errors > MAX_SELECT_ERRORS) {
  -                shutdown_in_progress = 1;
  -                ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
  -                             "Too many errors in select loop. Child process exiting.");
  -                break;
  -            }
  -	} else {
  -	    ap_listen_rec *lr;
  -
  -	    lr = find_ready_listener(&main_fds);
  -	    if (lr != NULL) {
  -                /* fetch the native socket descriptor */
  -                apr_os_sock_get(&nsd, lr->sd);
  -	    }
  -	}
  -
  -	do {
  -            clen = sizeof(sa_client);
  -            csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
  -            if (csd == INVALID_SOCKET) {
  -                csd = -1;
  -            }
  -        } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
  -
  -	if (csd < 0) {
  -            if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
  -		ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
  -			    "accept: (client socket)");
  -            }
  -	}
  -	else {
  -	    add_job(csd);
  -	}
  -    }
  -    SetEvent(exit_event);
  -}
  -
  -static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
  -{
  -    apr_os_sock_info_t sockinfo;
  -    int len;
  -
  -    if (context == NULL) {
  -        /* allocate the completion context and the transaction pool */
  -        context = apr_pcalloc(pconf, sizeof(COMP_CONTEXT));
  -        apr_pool_create(&context->ptrans, pchild);
  -        apr_pool_tag(context->ptrans, "ptrans");
  -        context->ba = apr_bucket_alloc_create(pchild);
  -    }
  -    
  -    while (1) {
  -        apr_pool_clear(context->ptrans);        
  -        context->accept_socket = remove_job();
  -        if (context->accept_socket == -1) {
  -            return NULL;
  -        }
  -	len = sizeof(struct sockaddr);
  -        context->sa_server = apr_palloc(context->ptrans, len);
  -        if (getsockname(context->accept_socket, 
  -                        context->sa_server, &len)== SOCKET_ERROR) {
  -            ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, 
  -                         "getsockname failed");
  -            continue;
  -        }
  -        len = sizeof(struct sockaddr);
  -        context->sa_client = apr_palloc(context->ptrans, len);
  -        if ((getpeername(context->accept_socket,
  -                         context->sa_client, &len)) == SOCKET_ERROR) {
  -            ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, 
  -                         "getpeername failed");
  -            memset(&context->sa_client, '\0', sizeof(context->sa_client));
  -        }
  -        sockinfo.os_sock = &context->accept_socket;
  -        sockinfo.local   = context->sa_server;
  -        sockinfo.remote  = context->sa_client;
  -        sockinfo.family  = APR_INET;
  -        sockinfo.type    = SOCK_STREAM;
  -        apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
  -
  -        return context;
  -    }
  -}
  -/* Windows NT/2000 specific code...
  - * Accept processing for on Windows NT uses a producer/consumer queue 
  - * model. An accept thread accepts connections off the network then issues
  - * PostQueuedCompletionStatus() to awake a thread blocked on the ThreadDispatch 
  - * IOCompletionPort.
  - *
  - * winnt_accept()
  - *    One or more accept threads run in this function, each of which accepts 
  - *    connections off the network and calls PostQueuedCompletionStatus() to
  - *    queue an io completion packet to the ThreadDispatch IOCompletionPort.
  - * winnt_get_connection()
  - *    Worker threads block on the ThreadDispatch IOCompletionPort awaiting 
  - *    connections to service.
  - */
  -static void winnt_accept(void *lr_) 
  -{
  -    ap_listen_rec *lr = (ap_listen_rec *)lr_;
  -    apr_os_sock_info_t sockinfo;
  -    PCOMP_CONTEXT context = NULL;
  -    DWORD BytesRead;
  -    SOCKET nlsd;
  -    int rv;
  -
  -    apr_os_sock_get(&nlsd, lr->sd);
  -
  -    while (!shutdown_in_progress) {
  -        if (!context) {
  -            context = mpm_get_completion_context();
  -            if (!context) {
  -                /* Temporary resource constraint? */
  -                Sleep(0);
  -                continue;
  -            }
  -        }
  -
  -        /* Create and initialize the accept socket */
  -        if (context->accept_socket == INVALID_SOCKET) {
  -            context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  -            if (context->accept_socket == INVALID_SOCKET) {
  -                /* Another temporary condition? */
  -                ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
  -                             "winnt_accept: Failed to allocate an accept socket. "
  -                             "Temporary resource constraint? Try again.");
  -                Sleep(100);
  -                continue;
  -            }
  -        }
  -
  -        /* AcceptEx on the completion context. The completion context will be 
  -         * signaled when a connection is accepted. 
  -         */
  -        if (!AcceptEx(nlsd, context->accept_socket,
  -                      context->buff,
  -                      0,
  -                      PADDED_ADDR_SIZE, 
  -                      PADDED_ADDR_SIZE,
  -                      &BytesRead,
  -                      &context->Overlapped)) {
  -            rv = apr_get_netos_error();
  -            if (rv == APR_FROM_OS_ERROR(WSAEINVAL)) {
  -                /* Hack alert. Occasionally, TransmitFile will not recycle the 
  -                 * accept socket (usually when the client disconnects early). 
  -                 * Get a new socket and try the call again.
  -                 */
  -                closesocket(context->accept_socket);
  -                context->accept_socket = INVALID_SOCKET;
  -                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
  -                       "winnt_accept: AcceptEx failed due to early client "
  -                       "disconnect. Reallocate the accept socket and try again.");
  -                continue;
  -            }
  -            else if (rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
  -                ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
  -                             "winnt_accept: AcceptEx failed. Attempting to recover.");
  -                closesocket(context->accept_socket);
  -                context->accept_socket = INVALID_SOCKET;
  -                Sleep(100);
  -                continue;
  -            }
  -
  -            /* Wait for pending i/o. Wake up once per second to check for shutdown */
  -            while (1) {
  -                rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
  -                if (rv == WAIT_OBJECT_0) {
  -                    if (!GetOverlappedResult(context->Overlapped.hEvent, 
  -                                             &context->Overlapped, 
  -                                             &BytesRead, FALSE)) {
  -                        ap_log_error(APLOG_MARK,APLOG_WARNING, GetLastError(), ap_server_conf,
  -                                     "winnt_accept: Asynchronous AcceptEx failed.");
  -                        closesocket(context->accept_socket);
  -                        context->accept_socket = INVALID_SOCKET;
  -                    }
  -                    break;
  -                }
  -                /* WAIT_TIMEOUT */
  -                if (shutdown_in_progress) {
  -                    closesocket(context->accept_socket);
  -                    context->accept_socket = INVALID_SOCKET;
  -                    break;
  -                }
  -            }
  -            if (context->accept_socket == INVALID_SOCKET) {
  -                continue;
  -            }
  -        }
  -
  -        /* Inherit the listen socket settings. Required for 
  -         * shutdown() to work 
  -         */
  -        if (setsockopt(context->accept_socket, SOL_SOCKET,
  -                       SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
  -                       sizeof(nlsd))) {
  -            ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
  -                         "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
  -            /* Not a failure condition. Keep running. */
  -        }
  -
  -        /* Get the local & remote address */
  -        GetAcceptExSockaddrs(context->buff,
  -                             0,
  -                             PADDED_ADDR_SIZE,
  -                             PADDED_ADDR_SIZE,
  -                             &context->sa_server,
  -                             &context->sa_server_len,
  -                             &context->sa_client,
  -                             &context->sa_client_len);
  -
  -        sockinfo.os_sock = &context->accept_socket;
  -        sockinfo.local   = context->sa_server;
  -        sockinfo.remote  = context->sa_client;
  -        sockinfo.family  = APR_INET;
  -        sockinfo.type    = SOCK_STREAM;
  -        apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
  -
  -        /* When a connection is received, send an io completion notification to
  -         * the ThreadDispatchIOCP. This function could be replaced by
  -         * mpm_post_completion_context(), but why do an extra function call...
  -         */
  -        PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
  -                                   &context->Overlapped);
  -        context = NULL;
  -    }
  -    if (!shutdown_in_progress) {
  -        /* Yow, hit an irrecoverable error! Tell the child to die. */
  -        SetEvent(exit_event);
  -    }
  -    ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
  -                 "Child %d: Accept thread exiting.", my_pid);
  -}
  -static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
  -{
  -    int rc;
  -    DWORD BytesRead;
  -    DWORD CompKey;
  -    LPOVERLAPPED pol;
  -
  -    mpm_recycle_completion_context(context);
  -
  -    apr_atomic_inc(&g_blocked_threads);
  -    while (1) {
  -        if (workers_may_exit) {
  -            apr_atomic_dec(&g_blocked_threads);
  -            return NULL;
  -        }
  -        rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
  -                                       &pol, INFINITE);
  -        if (!rc) {
  -            rc = apr_get_os_error();
  -            ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
  -                             "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc);
  -            continue;
  -        }
  -
  -        switch (CompKey) {
  -        case IOCP_CONNECTION_ACCEPTED:
  -            context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
  -            break;
  -        case IOCP_SHUTDOWN:
  -            apr_atomic_dec(&g_blocked_threads);
  -            return NULL;
  -        default:
  -            apr_atomic_dec(&g_blocked_threads);
  -            return NULL;
  -        }
  -        break;
  -    }
  -    apr_atomic_dec(&g_blocked_threads);
  -
  -    return context;
  -}
  -
  -/*
  - * worker_main()
  - * Main entry point for the worker threads. Worker threads block in 
  - * win*_get_connection() awaiting a connection to service.
  - */
  -static void worker_main(long thread_num)
  -{
  -    static int requests_this_child = 0;
  -    PCOMP_CONTEXT context = NULL;
  -    ap_sb_handle_t *sbh;
  -
  -    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
  -                 "Child %d: Worker thread %d starting.", my_pid, thread_num);
  -    while (1) {
  -        conn_rec *c;
  -        apr_int32_t disconnected;
  -
  -        ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
  -
  -        /* Grab a connection off the network */
  -        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
  -            context = win9x_get_connection(context);
  -        }
  -        else {
  -            context = winnt_get_connection(context);
  -        }
  -        if (!context) {
  -            /* Time for the thread to exit */
  -            break;
  -        }
  -
  -        /* Have we hit MaxRequestPerChild connections? */
  -        if (ap_max_requests_per_child) {
  -            requests_this_child++;
  -            if (requests_this_child > ap_max_requests_per_child) {
  -                SetEvent(max_requests_per_child_event);
  -            }
  -        }
  -
  -        ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
  -        c = ap_run_create_connection(context->ptrans, ap_server_conf,
  -                                     context->sock, thread_num, sbh,
  -                                     context->ba);
  -
  -        if (c) {
  -            ap_process_connection(c, context->sock);
  -            apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED, 
  -                               &disconnected);
  -            if (!disconnected) {
  -                context->accept_socket = INVALID_SOCKET;
  -                ap_lingering_close(c);
  -            }
  -        }
  -        else {
  -            /* ap_run_create_connection closes the socket on failure */
  -            context->accept_socket = INVALID_SOCKET;
  -        }
  -    }
  -
  -    ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, 
  -                                        (request_rec *) NULL);
  -
  -    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
  -                 "Child %d: Worker thread %d exiting.", my_pid, thread_num);
  -}
  -
  -static void cleanup_thread(thread *handles, int *thread_cnt, int thread_to_clean)
  -{
  -    int i;
  -
  -    CloseHandle(handles[thread_to_clean]);
  -    for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
  -	handles[i] = handles[i + 1];
  -    (*thread_cnt)--;
  -}
  -
  -/*
  - * child_main() 
  - * Entry point for the main control thread for the child process. 
  - * This thread creates the accept thread, worker threads and
  - * monitors the child process for maintenance and shutdown
  - * events.
  - */
  -static void create_listener_thread()
  -{
  -    int tid;
  -    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
  -        _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) win9x_accept,
  -                       NULL, 0, &tid);
  -    } else {
  -        /* Start an accept thread per listener 
  -         * XXX: Why would we have a NULL sd in our listeners?
  -         */
  -        ap_listen_rec *lr;
  -        for (lr = ap_listeners; lr; lr = lr->next) {
  -            if (lr->sd != NULL) {
  -                _beginthreadex(NULL, 1000, (LPTHREAD_START_ROUTINE) winnt_accept,
  -                               (void *) lr, 0, &tid);
  -            }
  -        }
  -    }
  -}
  -static void child_main()
  -{
  -    apr_status_t status;
  -    apr_hash_t *ht;
  -    ap_listen_rec *lr;
  -    HANDLE child_events[2];
  -    int threads_created = 0;
  -    int listener_started = 0;
  -    int tid;
  -    thread *child_handles;
  -    int rv;
  -    time_t end_time;
  -    int i;
  -    int cld;
  -
  -    apr_pool_create(&pchild, pconf);
  -    apr_pool_tag(pchild, "pchild");
  -
  -    ap_run_child_init(pchild, ap_server_conf);
  -    ht = apr_hash_make(pchild);
  -
  -    /* Initialize the child_events */
  -    max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL);
  -    if (!max_requests_per_child_event) {
  -        ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
  -                     "Child %d: Failed to create a max_requests event.", my_pid);
  -        exit(APEXIT_CHILDINIT);
  -    }
  -    child_events[0] = exit_event;
  -    child_events[1] = max_requests_per_child_event;
  -
  -    allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
  -    apr_thread_mutex_create(&allowed_globals.jobmutex, 
  -                            APR_THREAD_MUTEX_DEFAULT, pchild);
  -
  -    /*
  -     * Wait until we have permission to start accepting connections.
  -     * start_mutex is used to ensure that only one child ever
  -     * goes into the listen/accept loop at once.
  -     */
  -    status = apr_proc_mutex_lock(start_mutex);
  -    if (status != APR_SUCCESS) {
  -        ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
  -                     "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
  -        exit(APEXIT_CHILDINIT);
  -    }
  -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
  -                 "Child %d: Acquired the start mutex.", my_pid);
  -
  -    /*
  -     * Create the worker thread dispatch IOCompletionPort
  -     * on Windows NT/2000
  -     */
  -    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
  -        /* Create the worker thread dispatch IOCP */
  -        ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
  -                                                    NULL,
  -                                                    0,
  -                                                    0); /* CONCURRENT ACTIVE THREADS */
  -        apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
  -    }
  -
  -    /* 
  -     * Create the pool of worker threads
  -     */
  -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
  -                 "Child %d: Starting %d worker threads.", my_pid, ap_threads_per_child);
  -    child_handles = (thread) apr_pcalloc(pchild, ap_threads_per_child * sizeof(int));
  -    while (1) {
  -        for (i = 0; i < ap_threads_per_child; i++) {
  -            int *score_idx;
  -            int status = ap_scoreboard_image->servers[0][i].status;
  -            if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
  -                continue;
  -            }
  -            ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
  -            child_handles[i] = (thread) _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) worker_main,
  -                                                       (void *) i, 0, &tid);
  -            if (child_handles[i] == 0) {
  -                ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
  -                             "Child %d: _beginthreadex failed. Unable to create all worker threads. "
  -                             "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.", 
  -                             threads_created, ap_threads_per_child);
  -                ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
  -                goto shutdown;
  -            }
  -            threads_created++;
  -            /* Save the score board index in ht keyed to the thread handle. We need this 
  -             * when cleaning up threads down below...
  -             */
  -            score_idx = apr_pcalloc(pchild, sizeof(int));
  -            *score_idx = i;
  -            apr_hash_set(ht, &child_handles[i], sizeof(thread), score_idx);
  -        }
  -        /* Start the listener only when workers are available */
  -        if (!listener_started && threads_created) {
  -            create_listener_thread();
  -            listener_started = 1;
  -        }
  -        if (threads_created == ap_threads_per_child) {
  -            break;
  -        }
  -        /* Check to see if the child has been told to exit */
  -        if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
  -            break;
  -        }
  -        /* wait for previous generation to clean up an entry in the scoreboard */
  -        apr_sleep(1 * APR_USEC_PER_SEC);
  -    }
  -
  -    /* Wait for one of three events:
  -     * exit_event: 
  -     *    The exit_event is signaled by the parent process to notify 
  -     *    the child that it is time to exit.
  -     *
  -     * max_requests_per_child_event: 
  -     *    This event is signaled by the worker threads to indicate that
  -     *    the process has handled MaxRequestsPerChild connections.
  -     *
  -     * TIMEOUT:
  -     *    To do periodic maintenance on the server (check for thread exits,
  -     *    number of completion contexts, etc.)
  -     */
  -    while (1) {
  -        rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
  -        cld = rv - WAIT_OBJECT_0;
  -        if (rv == WAIT_FAILED) {
  -            /* Something serious is wrong */
  -            ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
  -                         "Child %d: WAIT_FAILED -- shutting down server");
  -            break;
  -        }
  -        else if (rv == WAIT_TIMEOUT) {
  -            apr_proc_other_child_check();
  -        }
  -        else if (cld == 0) {
  -            /* Exit event was signaled */
  -            ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
  -                         "Child %d: Exit event signaled. Child process is ending.", my_pid);
  -            break;
  -        }
  -        else {
  -            /* MaxRequestsPerChild event set by the worker threads.
  -             * Signal the parent to restart
  -             */
  -            ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
  -                         "Child %d: Process exiting because it reached "
  -                         "MaxRequestsPerChild. Signaling the parent to "
  -                         "restart a new child process.", my_pid);
  -            ap_signal_parent(SIGNAL_PARENT_RESTART);
  -            break;
  -        }
  -    }
  -
  -    /* 
  -     * Time to shutdown the child process 
  -     */
  -
  - shutdown:
  -    /* Setting is_graceful will cause threads handling keep-alive connections 
  -     * to close the connection after handling the current request.
  -     */
  -    is_graceful = 1;
  -
  -    /* Close the listening sockets. Note, we must close the listeners
  -     * before closing any accept sockets pending in AcceptEx to prevent
  -     * memory leaks in the kernel.
  -     */
  -    for (lr = ap_listeners; lr ; lr = lr->next) {
  -        apr_socket_close(lr->sd);
  -    }
  -
  -    /* Shutdown listener threads and pending AcceptEx socksts 
  -     * but allow the worker threads to continue consuming from
  -     * the queue of accepted connections.
  -     */
  -    shutdown_in_progress = 1;
  -
  -    Sleep(1000);
  -
  -    /* Tell the worker threads to exit */
  -    workers_may_exit = 1;
  -
  -    /* Release the start_mutex to let the new process (in the restart
  -     * scenario) a chance to begin accepting and servicing requests 
  -     */
  -    rv = apr_proc_mutex_unlock(start_mutex);
  -    if (rv == APR_SUCCESS) {
  -        ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf, 
  -                     "Child %d: Released the start mutex", my_pid);
  -    }
  -    else {
  -        ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, 
  -                     "Child %d: Failure releasing the start mutex", my_pid);
  -    }
  -
  -    /* Shutdown the worker threads */
  -    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
  -        for (i = 0; i < threads_created; i++) {
  -            add_job(-1);
  -        }
  -    }
  -    else { /* Windows NT/2000 */
  -        /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
  -        while (g_blocked_threads > 0) {
  -            ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf, 
  -                         "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
  -            for (i=g_blocked_threads; i > 0; i--) {
  -                PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
  -            }
  -            Sleep(1000);
  -        }
  -        /* Empty the accept queue of completion contexts */
  -        apr_thread_mutex_lock(qlock);
  -        while (qhead) {
  -            CloseHandle(qhead->Overlapped.hEvent);
  -            closesocket(qhead->accept_socket);
  -            qhead = qhead->next;
  -        }
  -        apr_thread_mutex_unlock(qlock);
  -    }
  -
  -    /* Give busy worker threads a chance to service their connections */
  -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
  -                 "Child %d: Waiting for %d worker threads to exit.", my_pid, threads_created);
  -    end_time = time(NULL) + 180;
  -    while (threads_created) {
  -        rv = wait_for_many_objects(threads_created, child_handles, end_time - time(NULL));
  -        if (rv != WAIT_TIMEOUT) {
  -            rv = rv - WAIT_OBJECT_0;
  -            ap_assert((rv >= 0) && (rv < threads_created));
  -            cleanup_thread(child_handles, &threads_created, rv);
  -            continue;
  -        }
  -        break;
  -    }
  -
  -    /* Kill remaining threads off the hard way */
  -    if (threads_created) {
  -        ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
  -                     "Child %d: Terminating %d threads that failed to exit.", my_pid);
  -    }
  -    for (i = 0; i < threads_created; i++) {
  -        int *score_idx;
  -        TerminateThread(child_handles[i], 1);
  -        CloseHandle(child_handles[i]);
  -        /* Reset the scoreboard entry for the thread we just whacked */
  -        score_idx = apr_hash_get(ht, &child_handles[i], sizeof(thread));
  -        ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL);        
  -    }
  -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
  -                 "Child %d: All worker threads have exited.", my_pid);
  -
  -    CloseHandle(allowed_globals.jobsemaphore);
  -    apr_thread_mutex_destroy(allowed_globals.jobmutex);
  -    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
  -    	apr_thread_mutex_destroy(qlock);
  -
  -    apr_pool_destroy(pchild);
  -    CloseHandle(exit_event);
  -}
   
   static int send_handles_to_child(apr_pool_t *p, 
                                    HANDLE child_ready_event,
  @@ -1542,6 +446,83 @@
       return 0;
   }
   
  +
  +/* 
  + * get_listeners_from_parent()
  + * The listen sockets are opened in the parent. This function, which runs
  + * exclusively in the child process, receives them from the parent and
  + * makes them availeble in the child.
  + */
  +void get_listeners_from_parent(server_rec *s)
  +{
  +    WSAPROTOCOL_INFO WSAProtocolInfo;
  +    HANDLE pipe;
  +    ap_listen_rec *lr;
  +    DWORD BytesRead;
  +    int lcnt = 0;
  +    SOCKET nsd;
  +
  +    /* Set up a default listener if necessary */
  +    if (ap_listeners == NULL) {
  +        ap_listen_rec *lr;
  +        lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
  +        lr->sd = NULL;
  +        lr->next = ap_listeners;
  +        ap_listeners = lr;
  +    }
  +
  +    /* Open the pipe to the parent process to receive the inherited socket
  +     * data. The sockets have been set to listening in the parent process.
  +     */
  +    pipe = GetStdHandle(STD_INPUT_HANDLE);
  +
  +    for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
  +        if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), 
  +                      &BytesRead, (LPOVERLAPPED) NULL)) {
  +            ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
  +                         "setup_inherited_listeners: Unable to read socket data from parent");
  +            exit(APEXIT_CHILDINIT);
  +        }
  +        nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
  +                        &WSAProtocolInfo, 0, 0);
  +        if (nsd == INVALID_SOCKET) {
  +            ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
  +                         "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
  +            exit(APEXIT_CHILDINIT);
  +        }
  +
  +        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
  +            HANDLE hProcess = GetCurrentProcess();
  +            HANDLE dup;
  +            if (DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup, 
  +                                0, FALSE, DUPLICATE_SAME_ACCESS)) {
  +                closesocket(nsd);
  +                nsd = (SOCKET) dup;
  +            }
  +        }
  +        else {
  +            /* A different approach.  Many users report errors such as 
  +             * (32538)An operation was attempted on something that is not 
  +             * a socket.  : Parent: WSADuplicateSocket failed...
  +             *
  +             * This appears that the duplicated handle is no longer recognized
  +             * as a socket handle.  SetHandleInformation should overcome that
  +             * problem by not altering the handle identifier.  But this won't
  +             * work on 9x - it's unsupported.
  +             */
  +            if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
  +                ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
  +                             "set_listeners_noninheritable: SetHandleInformation failed.");
  +            }
  +        }
  +        apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
  +    }
  +
  +    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
  +                 "Child %d: retrieved %d listeners from parent", my_pid, lcnt);
  +}
  +
  +
   static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId, 
                                      apr_file_t *child_in)
   {
  @@ -2508,10 +1489,6 @@
           return DONE;
       }
   
  -    if (!set_listeners_noninheritable(s->process->pool)) {
  -        return 1;
  -    }
  -
       return OK;
   }
   
  @@ -2524,7 +1501,7 @@
       /* This is a child process, not in single process mode */
       if (!one_process) {
           /* Set up events and the scoreboard */
  -        get_handles_from_parent(s);
  +        get_handles_from_parent(s, ap_scoreboard_shm);
   
           /* Set up the listeners */
           get_listeners_from_parent(s);
  @@ -2573,7 +1550,7 @@
           ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
                        "Child %d: Child process is running", my_pid);
   
  -        child_main();
  +        child_main(pconf);
   
           ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
                        "Child %d: Child process is exiting", my_pid);        
  @@ -2631,3 +1608,5 @@
       winnt_cmds,		        /* command apr_table_t */
       winnt_hooks 		/* register_hooks */
   };
  +
  +#endif /* def WIN32 */
  
  
  
  1.39      +0 -43     httpd-2.0/server/mpm/winnt/mpm_winnt.h
  
  Index: mpm_winnt.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.h,v
  retrieving revision 1.38
  retrieving revision 1.39
  diff -u -r1.38 -r1.39
  --- mpm_winnt.h	24 Jun 2002 07:53:50 -0000	1.38
  +++ mpm_winnt.h	29 Jul 2002 05:12:50 -0000	1.39
  @@ -113,49 +113,6 @@
   } ap_signal_parent_e;
   AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type);
   
  -/* This code is stolen from the apr_private.h and misc/win32/misc.c
  - * Please see those sources for detailed documentation.
  - */
  -typedef enum {
  -    DLL_WINBASEAPI = 0,    // kernel32 From WinBase.h
  -    DLL_WINADVAPI = 1,     // advapi32 From WinBase.h
  -    DLL_WINSOCKAPI = 2,    // mswsock  From WinSock.h
  -    DLL_WINSOCK2API = 3,   // ws2_32   From WinSock2.h
  -    DLL_defined = 4        // must define as last idx_ + 1
  -} ap_dlltoken_e;
  -
  -FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char *fnName, int ordinal);
  -
  -#define AP_DECLARE_LATE_DLL_FUNC(lib, rettype, calltype, fn, ord, args, names) \
  -    typedef rettype (calltype *ap_winapi_fpt_##fn) args; \
  -    static ap_winapi_fpt_##fn ap_winapi_pfn_##fn = NULL; \
  -    __inline rettype ap_winapi_##fn args \
  -    {   if (!ap_winapi_pfn_##fn) \
  -            ap_winapi_pfn_##fn = (ap_winapi_fpt_##fn) ap_load_dll_func(lib, #fn, ord); \
  -        return (*(ap_winapi_pfn_##fn)) names; }; \
  -
  -/* Win2K kernel only */
  -AP_DECLARE_LATE_DLL_FUNC(DLL_WINADVAPI, BOOL, WINAPI, ChangeServiceConfig2A, 0, (
  -    SC_HANDLE hService, 
  -    DWORD dwInfoLevel, 
  -    LPVOID lpInfo),
  -    (hService, dwInfoLevel, lpInfo));
  -#undef ChangeServiceConfig2
  -#define ChangeServiceConfig2 ap_winapi_ChangeServiceConfig2A
  -
  -/* WinNT kernel only */
  -AP_DECLARE_LATE_DLL_FUNC(DLL_WINBASEAPI, BOOL, WINAPI, CancelIo, 0, (
  -    IN HANDLE hFile),
  -    (hFile));
  -#define CancelIo ap_winapi_CancelIo
  -
  -/* Win9x kernel only */
  -AP_DECLARE_LATE_DLL_FUNC(DLL_WINBASEAPI, DWORD, WINAPI, RegisterServiceProcess, 0, (
  -    DWORD dwProcessId,
  -    DWORD dwType),
  -    (dwProcessId, dwType));
  -#define RegisterServiceProcess ap_winapi_RegisterServiceProcess
  -
   /*
    * The Windoes MPM uses a queue of completion contexts that it passes
    * between the accept threads and the worker threads. Declare the
  
  
  

RE: cvs commit: httpd-2.0/server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h

Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
At 08:15 AM 7/29/2002, you wrote:
>Seperating out the routines that run only in the child (and putting them in
>child.c) is not a bad thing but this patch is difficult to review for
>several reasons:
>
>1. The commit log did not mention the biggest change. Easy to intuit looking
>at the code, but it should have at least been mentioned in the commit log.

The changes to fold out the make_listeners_noninherited should really have
been entirely precommitted, not part of the split.  I didn't foresee the 
problems
until I was quite a ways through, and unfortunately this box has only one tree.

Bigger problem, though.  This body of this change should have occurred with
the original child.c checkin.  That's my bad for missing the fact that it 
hadn't.


>2. You broke the rule to not make more than one conceptual change to the
>code within a single commit. You moved code (to child.c) and made a function
>change (which pools were being used for what) in one commit. These changes
>should have been made as two seperate commits (or perhaps 3 or 4) to make
>review a bit less onorous.

This patch was supposed to be -only- the change to pass [instead of stash]
the pconf for pchild.  I note that pchild is the direct relative of pconf, 
so in the
Win95 queue code, I presumed that pchild is just as safe for that application.
The *intended* patch for mpm_winnt.c simply changed child_main() to pass
the pconf.

Please read the child.c patch on it's own, and forgive that mpm_winnt changes
were all slated (except for the child_main() pconf arg) for the child.c 
checkin.

> > I am chewing on my tounge now to not be nasty.  Where are you going with
> > this?  Perhaps I missed it but was this change discussed on list?  And the
> > commit log message describing the change is quite poor.

The prior commit message is the one you should be interested in.

mpm_winnt.c is now just about as difficult to follow as the old src/main.c.
Our multiuse of globals makes my head spin.  Globals are also quite
dangerous with pool scopeing, it's hard to tell just when they will expire.

I reached the conclusion to attack this split when I ran into the issue of
factoring out the redundant max_requests_per_child_event ... which is
truly a special case of exit_event.  Such a special case that it should
fall into a single enum value in the same grouping as is_graceful.  But we
are inconsistent about how we toggle the various flags prior to signaling
the exit_event, and then wait until we are in the exit_event to toggle
is_graceful.  This is all quite hard to follow.

The real PITA was that I recognized that the parent doesn't know about
the child giving up the ghost until it's dead.  This is a trivial change; we
simply need to keep watching the exit event handle in the parent, as well,
and make it a manual reset event.  If the child's exit event is signalled,
the parent will know we have a child heading out of existence, and we
can preemptively begin a new child process while the old one cycles down.

> > > wrowe       2002/07/28 22:12:50
> > >
> > >   Modified:    server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h
> > >   Log:
> > >     pconf global factors out nicely.  The one other pconf appears to be
> > >     eqivilant to pchild.

The -huge- problem was that this was not a balanced commit.  My fault
that I didn't notice that mpm_winnt change itself was not within the 
following commit; I've added the following comment to mpm_winnt.c's and 
.h's logs.

   Modified:    .        libhttpd.dsp
                os/win32 ap_regkey.c os.h util_win32.c
   Added:       server/mpm/winnt child.c
   Log:
     Refactor out the child behavior from mpm_winnt.  This is the first
     step in making a legible multiprocess windows mpm, or at least
     structuring the code to always begin a new child as an old one is
     going to die soon, rather than waiting for it's final dying breath.

     The only code that had to be affected [due to the split and general
     structure of the code] was merging the set_listeners_noninherited()
     code directly into the get_listeners_from_parent code, and also into
     the apr socket.c code for winnt.  For the most part the code splits
     rather nicely.




RE: cvs commit: httpd-2.0/server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h

Posted by Bill Stoddard <bi...@wstoddard.com>.
Seperating out the routines that run only in the child (and putting them in
child.c) is not a bad thing but this patch is difficult to review for
several reasons:

1. The commit log did not mention the biggest change. Easy to intuit looking
at the code, but it should have at least been mentioned in the commit log.

2. You broke the rule to not make more than one conceptual change to the
code within a single commit. You moved code (to child.c) and made a function
change (which pools were being used for what) in one commit. These changes
should have been made as two seperate commits (or perhaps 3 or 4) to make
review a bit less onorous.

Bill

>
> Bill,
> I am chewing on my tounge now to not be nasty.  Where are you going with
> this?  Perhaps I missed it but was this change discussed on list?  And the
> commit log message describing the change is quite poor.
>
> Bill
>
> > -----Original Message-----
> > From: wrowe@apache.org [mailto:wrowe@apache.org]
> > Sent: Monday, July 29, 2002 1:13 AM
> > To: httpd-2.0-cvs@apache.org
> > Subject: cvs commit: httpd-2.0/server/mpm/winnt child.c mpm_winnt.c
> > mpm_winnt.h
> >
> >
> > wrowe       2002/07/28 22:12:50
> >
> >   Modified:    server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h
> >   Log:
> >     pconf global factors out nicely.  The one other pconf appears to be
> >     eqivilant to pchild.
> >
> >   Revision  Changes    Path
> >   1.2       +3 -4      httpd-2.0/server/mpm/winnt/child.c
> >
>> >
>


RE: cvs commit: httpd-2.0/server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h

Posted by Bill Stoddard <bi...@wstoddard.com>.
Bill,
I am chewing on my tounge now to not be nasty.  Where are you going with
this?  Perhaps I missed it but was this change discussed on list?  And the
commit log message describing the change is quite poor.

Bill

> -----Original Message-----
> From: wrowe@apache.org [mailto:wrowe@apache.org]
> Sent: Monday, July 29, 2002 1:13 AM
> To: httpd-2.0-cvs@apache.org
> Subject: cvs commit: httpd-2.0/server/mpm/winnt child.c mpm_winnt.c
> mpm_winnt.h
>
>
> wrowe       2002/07/28 22:12:50
>
>   Modified:    server/mpm/winnt child.c mpm_winnt.c mpm_winnt.h
>   Log:
>     pconf global factors out nicely.  The one other pconf appears to be
>     eqivilant to pchild.
>
>   Revision  Changes    Path
>   1.2       +3 -4      httpd-2.0/server/mpm/winnt/child.c
>
>   Index: child.c
>   ===================================================================
>   RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/child.c,v
>   retrieving revision 1.1
>   retrieving revision 1.2
>   diff -u -r1.1 -r1.2
>   --- child.c	29 Jul 2002 05:06:20 -0000	1.1
>   +++ child.c	29 Jul 2002 05:12:50 -0000	1.2
>   @@ -83,7 +83,6 @@
>
>    /* shared with mpm_winnt.c */
>    extern DWORD my_pid;
>   -extern apr_pool_t *pconf;
>
>    /* used by parent to signal the child to start and exit */
>    /* shared with mpm_winnt.c, but should be private to child.c */
>   @@ -97,7 +96,7 @@
>    /* Queue for managing the passing of COMP_CONTEXTs between
>     * the accept and worker threads.
>     */
>   -static apr_pool_t *pchild = NULL;
>   +static apr_pool_t *pchild;
>    static int shutdown_in_progress = 0;
>    static int workers_may_exit = 0;
>    static unsigned int g_blocked_threads = 0;
>   @@ -417,7 +416,7 @@
>
>        if (context == NULL) {
>            /* allocate the completion context and the transaction pool */
>   -        context = apr_pcalloc(pconf, sizeof(COMP_CONTEXT));
>   +        context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
>            apr_pool_create(&context->ptrans, pchild);
>            apr_pool_tag(context->ptrans, "ptrans");
>            context->ba = apr_bucket_alloc_create(pchild);
>   @@ -757,7 +756,7 @@
>    }
>
>
>   -void child_main()
>   +void child_main(apr_pool_t *pconf)
>    {
>        apr_status_t status;
>        apr_hash_t *ht;
>
>
>
>   1.290     +103 -1124 httpd-2.0/server/mpm/winnt/mpm_winnt.c
>
>   Index: mpm_winnt.c
>   ===================================================================
>   RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.c,v
>   retrieving revision 1.289
>   retrieving revision 1.290
>   diff -u -r1.289 -r1.290
>   --- mpm_winnt.c	27 Jul 2002 18:13:59 -0000	1.289
>   +++ mpm_winnt.c	29 Jul 2002 05:12:50 -0000	1.290
>   @@ -56,6 +56,8 @@
>     * University of Illinois, Urbana-Champaign.
>     */
>
>   +#ifdef WIN32
>   +
>    #define CORE_PRIVATE
>    #include "httpd.h"
>    #include "http_main.h"
>   @@ -108,19 +110,10 @@
>     */
>    extern apr_shm_t *ap_scoreboard_shm;
>    server_rec *ap_server_conf;
>   -typedef HANDLE thread;
>
>    /* Definitions of WINNT MPM specific config globals */
>   -apr_pool_t *pconf;
>   -static apr_pool_t *pchild = NULL;
>   -static int workers_may_exit = 0;
>   -static int shutdown_in_progress = 0;
>   -static unsigned int g_blocked_threads = 0;
>   -
>    static HANDLE shutdown_event;	/* used to signal the
> parent to shutdown */
>    static HANDLE restart_event;	/* used to signal the
> parent to restart */
>   -static HANDLE exit_event;       /* used by parent to signal
> the child to exit */
>   -static HANDLE max_requests_per_child_event;
>
>    static char ap_coredump_dir[MAX_STRING_LEN];
>
>   @@ -129,23 +122,31 @@
>
>    OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
>
>   -apr_proc_mutex_t *start_mutex;
>   -static DWORD my_pid;
>    static DWORD parent_pid;
>   +DWORD my_pid;
>
>    int ap_threads_per_child = 0;
>
>    /* ap_my_generation are used by the scoreboard code */
>    ap_generation_t volatile ap_my_generation=0;
>
>   -/* Queue for managing the passing of COMP_CONTEXTs between
>   - * the accept and worker threads.
>   +
>   +/* shared by service.c as global, although
>   + * perhaps it should be private.
>   + */
>   +apr_pool_t *pconf;
>   +
>   +
>   +/* definitions from child.c */
>   +void child_main(apr_pool_t *pconf);
>   +
>   +/* used by parent to signal the child to start and exit
>   + * NOTE: these are not sophisticated enough for multiple children
>   + * so they ultimately should not be shared with child.c
>     */
>   -static apr_thread_mutex_t  *qlock;
>   -static PCOMP_CONTEXT qhead = NULL;
>   -static PCOMP_CONTEXT qtail = NULL;
>   -static int num_completion_contexts = 0;
>   -static HANDLE ThreadDispatchIOCP = NULL;
>   +extern apr_proc_mutex_t *start_mutex;
>   +extern HANDLE exit_event;
>   +
>
>    /* Stub functions until this MPM supports the connection status API */
>
>   @@ -205,206 +206,6 @@
>    };
>
>
>   -AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context)
>   -{
>   -    /* Recycle the completion context.
>   -     * - clear the ptrans pool
>   -     * - put the context on the queue to be consumed by the
> accept thread
>   -     * Note:
>   -     * context->accept_socket may be in a disconnected but reusable
>   -     * state so -don't- close it.
>   -     */
>   -    if (context) {
>   -        apr_pool_clear(context->ptrans);
>   -        context->next = NULL;
>   -        ResetEvent(context->Overlapped.hEvent);
>   -        apr_thread_mutex_lock(qlock);
>   -        if (qtail)
>   -            qtail->next = context;
>   -        else
>   -            qhead = context;
>   -        qtail = context;
>   -        apr_thread_mutex_unlock(qlock);
>   -    }
>   -}
>   -
>   -AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void)
>   -{
>   -    apr_status_t rv;
>   -    PCOMP_CONTEXT context = NULL;
>   -
>   -    /* Grab a context off the queue */
>   -    apr_thread_mutex_lock(qlock);
>   -    if (qhead) {
>   -        context = qhead;
>   -        qhead = qhead->next;
>   -        if (!qhead)
>   -            qtail = NULL;
>   -    }
>   -    apr_thread_mutex_unlock(qlock);
>   -
>   -    /* If we failed to grab a context off the queue, alloc one out of
>   -     * the child pool. There may be up to ap_threads_per_child contexts
>   -     * in the system at once.
>   -     */
>   -    if (!context) {
>   -        if (num_completion_contexts >= ap_threads_per_child) {
>   -            static int reported = 0;
>   -            if (!reported) {
>   -                ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
> ap_server_conf,
>   -                             "Server ran out of threads to
> serve requests. Consider "
>   -                             "raising the ThreadsPerChild setting");
>   -                reported = 1;
>   -            }
>   -            return NULL;
>   -        }
>   -        /* Note:
>   -         * Multiple failures in the next two steps will cause
> the pchild pool
>   -         * to 'leak' storage. I don't think this is worth fixing...
>   -         */
>   -        context = (PCOMP_CONTEXT) apr_pcalloc(pchild,
> sizeof(COMP_CONTEXT));
>   -
>   -        context->Overlapped.hEvent = CreateEvent(NULL, TRUE,
> FALSE, NULL);
>   -        if (context->Overlapped.hEvent == NULL) {
>   -            /* Hopefully this is a temporary condition ... */
>   -            ap_log_error(APLOG_MARK,APLOG_WARNING,
> apr_get_os_error(), ap_server_conf,
>   -                         "mpm_get_completion_context:
> CreateEvent failed.");
>   -            return NULL;
>   -        }
>   -
>   -        /* Create the tranaction pool */
>   -        if ((rv = apr_pool_create(&context->ptrans, pchild))
> != APR_SUCCESS) {
>   -            ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
>   -                         "mpm_get_completion_context: Failed
> to create the transaction pool.");
>   -            CloseHandle(context->Overlapped.hEvent);
>   -            return NULL;
>   -        }
>   -        apr_pool_tag(context->ptrans, "ptrans");
>   -
>   -        context->accept_socket = INVALID_SOCKET;
>   -        context->ba = apr_bucket_alloc_create(pchild);
>   -        apr_atomic_inc(&num_completion_contexts);
>   -    }
>   -
>   -    return context;
>   -}
>   -
>   -AP_DECLARE(apr_status_t)
> mpm_post_completion_context(PCOMP_CONTEXT context,
>   -                                                     io_state_e state)
>   -{
>   -    LPOVERLAPPED pOverlapped;
>   -    if (context)
>   -        pOverlapped = &context->Overlapped;
>   -    else
>   -        pOverlapped = NULL;
>   -
>   -    PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state,
> pOverlapped);
>   -    return APR_SUCCESS;
>   -}
>   -
>   -/* This is the helper code to resolve late bound entry points
>   - * missing from one or more releases of the Win32 API...
>   - * but it sure would be nice if we didn't duplicate this code
>   - * from the APR ;-)
>   - */
>   -static const char* const lateDllName[DLL_defined] = {
>   -    "kernel32", "advapi32", "mswsock",  "ws2_32"  };
>   -static HMODULE lateDllHandle[DLL_defined] = {
>   -    NULL,       NULL,       NULL,       NULL      };
>   -
>   -FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char* fnName,
> int ordinal)
>   -{
>   -    if (!lateDllHandle[fnLib]) {
>   -        lateDllHandle[fnLib] = LoadLibrary(lateDllName[fnLib]);
>   -        if (!lateDllHandle[fnLib])
>   -            return NULL;
>   -    }
>   -    if (ordinal)
>   -        return GetProcAddress(lateDllHandle[fnLib], (char *) ordinal);
>   -    else
>   -        return GetProcAddress(lateDllHandle[fnLib], fnName);
>   -}
>   -
>   -/* To share the semaphores with other processes, we need a NULL ACL
>   - * Code from MS KB Q106387
>   - */
>   -static PSECURITY_ATTRIBUTES GetNullACL()
>   -{
>   -    PSECURITY_DESCRIPTOR pSD;
>   -    PSECURITY_ATTRIBUTES sa;
>   -
>   -    sa  = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR,
> sizeof(SECURITY_ATTRIBUTES));
>   -    sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES));
>   -
>   -    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
> SECURITY_DESCRIPTOR_MIN_LENGTH);
>   -    sa->lpSecurityDescriptor = pSD;
>   -
>   -    if (pSD == NULL || sa == NULL) {
>   -        return NULL;
>   -    }
>   -    apr_set_os_error(0);
>   -    if (!InitializeSecurityDescriptor(pSD,
> SECURITY_DESCRIPTOR_REVISION)
>   -	|| apr_get_os_error()) {
>   -        LocalFree( pSD );
>   -        LocalFree( sa );
>   -        return NULL;
>   -    }
>   -    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
>   -	|| apr_get_os_error()) {
>   -        LocalFree( pSD );
>   -        LocalFree( sa );
>   -        return NULL;
>   -    }
>   -
>   -    sa->bInheritHandle = TRUE;
>   -    return sa;
>   -}
>   -
>   -static void CleanNullACL( void *sa ) {
>   -    if( sa ) {
>   -        LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
>   -        LocalFree( sa );
>   -    }
>   -}
>   -
>   -/*
>   - * The Win32 call WaitForMultipleObjects will only allow you
> to wait for
>   - * a maximum of MAXIMUM_WAIT_OBJECTS (current 64).  Since the
> threading
>   - * model in the multithreaded version of apache wants to use
> this call,
>   - * we are restricted to a maximum of 64 threads.  This is a simplistic
>   - * routine that will increase this size.
>   - */
>   -static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE
> *lpHandles,
>   -                                   DWORD dwSeconds)
>   -{
>   -    time_t tStopTime;
>   -    DWORD dwRet = WAIT_TIMEOUT;
>   -    DWORD dwIndex=0;
>   -    BOOL bFirst = TRUE;
>   -
>   -    tStopTime = time(NULL) + dwSeconds;
>   -
>   -    do {
>   -        if (!bFirst)
>   -            Sleep(1000);
>   -        else
>   -            bFirst = FALSE;
>   -
>   -        for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS <
> nCount; dwIndex++) {
>   -            dwRet = WaitForMultipleObjects(
>   -                min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex *
> MAXIMUM_WAIT_OBJECTS)),
>   -                lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS),
>   -                0, 0);
>   -
>   -            if (dwRet != WAIT_TIMEOUT) {
>
>   -              break;
>   -            }
>   -        }
>   -    } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
>   -
>   -    return dwRet;
>   -}
>   -
>    /*
>     * Signalling Apache on NT.
>     *
>   @@ -450,7 +251,8 @@
>    	"%s_restart", signal_name_prefix);
>    }
>
>   -static int volatile is_graceful = 0;
>   +int volatile is_graceful = 0;
>   +
>    AP_DECLARE(int) ap_graceful_stop_signalled(void)
>    {
>        return is_graceful;
>   @@ -517,81 +319,6 @@
>        CloseHandle(e);
>    }
>
>   -/* set_listeners_noninheritable()
>   - * Make the listening socket handles noninheritable by processes
>   - * started out of this process.
>   - */
>   -static int set_listeners_noninheritable(apr_pool_t *p)
>   -{
>   -    ap_listen_rec *lr;
>   -    HANDLE dup;
>   -    SOCKET nsd;
>   -    HANDLE hProcess = GetCurrentProcess();
>   -
>   -    for (lr = ap_listeners; lr; lr = lr->next) {
>   -        apr_os_sock_get(&nsd,lr->sd);
>   -        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
>   -            if (!DuplicateHandle(hProcess, (HANDLE) nsd,
> hProcess, &dup,
>   -                                 0, FALSE, DUPLICATE_SAME_ACCESS)) {
>   -                ap_log_error(APLOG_MARK, APLOG_ERR,
> apr_get_os_error(), ap_server_conf,
>   -                             "set_listeners_noninheritable:
> DuplicateHandle failed.");
>   -            }
>   -            else {
>   -                closesocket(nsd);
>   -                nsd = (SOCKET) dup;
>   -                apr_os_sock_put(&lr->sd, &nsd, p);
>   -            }
>   -        }
>   -        else {
>   -            /* A different approach.  Many users report errors such as
>   -             * (32538)An operation was attempted on something
> that is not
>   -             * a socket.  : Parent: WSADuplicateSocket failed...
>   -             *
>   -             * This appears that the duplicated handle is no
> longer recognized
>   -             * as a socket handle.  SetHandleInformation
> should overcome that
>   -             * problem by not altering the handle identifier.
> But this won't
>   -             * work on 9x - it's unsupported.
>   -             */
>   -            if (!SetHandleInformation((HANDLE)nsd,
> HANDLE_FLAG_INHERIT, 0)) {
>   -                ap_log_error(APLOG_MARK, APLOG_ERR,
> apr_get_os_error(), ap_server_conf,
>   -                             "set_listeners_noninheritable:
> SetHandleInformation failed.");
>   -            }
>   -        }
>   -    }
>   -
>   -    if (my_pid == parent_pid) {
>   -        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
>   -                     "Parent: Marked listeners as not inheritable.");
>   -    } else {
>   -        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
>   -                     "Child %d: Marked listeners as not
> inheritable.", my_pid);
>   -    }
>   -    return 1;
>   -}
>   -
>   -/*
>   - * find_ready_listener()
>   - * Only used by Win9* and should go away when the
> win9*_accept() function is
>   - * reimplemented using apr_poll().
>   - */
>   -static ap_listen_rec *head_listener;
>   -static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
>   -{
>   -    ap_listen_rec *lr;
>   -    SOCKET nsd;
>   -
>   -    for (lr = head_listener; lr ; lr = lr->next) {
>   -        apr_os_sock_get(&nsd, lr->sd);
>   -	if (FD_ISSET(nsd, main_fds)) {
>   -	    head_listener = lr->next;
>   -            if (head_listener == NULL)
>   -                head_listener = ap_listeners;
>   -
>   -	    return (lr);
>   -	}
>   -    }
>   -    return NULL;
>   -}
>
>    /*
>     * Passed the following handles [in sync with send_handles_to_child()]
>   @@ -600,7 +327,7 @@
>     *   exit event  [save to poll later]
>     *   scoreboard shm handle [to recreate the ap_scoreboard]
>     */
>   -void get_handles_from_parent(server_rec *s)
>   +void get_handles_from_parent(server_rec *s, apr_shm_t *scoreboard_shm)
>    {
>        HANDLE pipe;
>        HANDLE hScore;
>   @@ -659,829 +386,6 @@
>                     "Child %d: Retrieved our scoreboard from the
> parent.", my_pid);
>    }
>
>   -/*
>   - * get_listeners_from_parent()
>   - * The listen sockets are opened in the parent. This function,
> which runs
>   - * exclusively in the child process, receives them from the parent and
>   - * makes them availeble in the child.
>   - */
>   -void get_listeners_from_parent(server_rec *s)
>   -{
>   -    WSAPROTOCOL_INFO WSAProtocolInfo;
>   -    HANDLE pipe;
>   -    ap_listen_rec *lr;
>   -    DWORD BytesRead;
>   -    int lcnt = 0;
>   -    SOCKET nsd;
>   -
>   -    /* Set up a default listener if necessary */
>   -    if (ap_listeners == NULL) {
>   -        ap_listen_rec *lr;
>   -        lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
>   -        lr->sd = NULL;
>   -        lr->next = ap_listeners;
>   -        ap_listeners = lr;
>   -    }
>   -
>   -    /* Open the pipe to the parent process to receive the
> inherited socket
>   -     * data. The sockets have been set to listening in the
> parent process.
>   -     */
>   -    pipe = GetStdHandle(STD_INPUT_HANDLE);
>   -
>   -    for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
>   -        if (!ReadFile(pipe, &WSAProtocolInfo,
> sizeof(WSAPROTOCOL_INFO),
>   -                      &BytesRead, (LPOVERLAPPED) NULL)) {
>   -            ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_os_error(), ap_server_conf,
>   -                         "setup_inherited_listeners: Unable to
> read socket data from parent");
>   -            exit(APEXIT_CHILDINIT);
>   -        }
>   -        nsd = WSASocket(FROM_PROTOCOL_INFO,
> FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
>   -                        &WSAProtocolInfo, 0, 0);
>   -        if (nsd == INVALID_SOCKET) {
>   -            ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_netos_error(), ap_server_conf,
>   -                         "Child %d:
> setup_inherited_listeners(), WSASocket failed to open the
> inherited socket.", my_pid);
>   -            exit(APEXIT_CHILDINIT);
>   -        }
>   -        apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
>   -    }
>   -
>   -    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
>   -                 "Child %d: retrieved %d listeners from
> parent", my_pid, lcnt);
>   -
>   -    if (!set_listeners_noninheritable(s->process->pool)) {
>   -        exit(APEXIT_CHILDINIT);
>   -    }
>   -}
>   -
>   -
>   -/* Windows 9x specific code...
>   - * Accept processing for on Windows 95/98 uses a
> producer/consumer queue
>   - * model. A single thread accepts connections and queues the
> accepted socket
>   - * to the accept queue for consumption by a pool of worker threads.
>   - *
>   - * win9x_accept()
>   - *    The accept threads runs this function, which accepts
> connections off
>   - *    the network and calls add_job() to queue jobs to the
> accept_queue.
>   - * add_job()/remove_job()
>   - *    Add or remove an accepted socket from the list of sockets
>   - *    connected to clients. allowed_globals.jobmutex protects
>   - *    against multiple concurrent access to the linked list of jobs.
>   - * win9x_get_connection()
>   - *    Calls remove_job() to pull a job from the accept queue.
> All the worker
>   - *    threads block on remove_job.
>   - */
>   -
>   -typedef struct joblist_s {
>   -    struct joblist_s *next;
>   -    int sock;
>   -} joblist;
>   -
>   -typedef struct globals_s {
>   -    HANDLE jobsemaphore;
>   -    joblist *jobhead;
>   -    joblist *jobtail;
>   -    apr_thread_mutex_t *jobmutex;
>   -    int jobcount;
>   -} globals;
>   -
>   -globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
>   -#define MAX_SELECT_ERRORS 100
>   -
>   -static void add_job(int sock)
>   -{
>   -    joblist *new_job;
>   -
>   -    new_job = (joblist *) malloc(sizeof(joblist));
>   -    if (new_job == NULL) {
>   -	ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
>   -                     "Ouch!  Out of memory in add_job()!");
>   -        return;
>   -    }
>   -    new_job->next = NULL;
>   -    new_job->sock = sock;
>   -
>   -    apr_thread_mutex_lock(allowed_globals.jobmutex);
>   -
>   -    if (allowed_globals.jobtail != NULL)
>   -	allowed_globals.jobtail->next = new_job;
>   -    allowed_globals.jobtail = new_job;
>   -    if (!allowed_globals.jobhead)
>   -	allowed_globals.jobhead = new_job;
>   -    allowed_globals.jobcount++;
>   -    ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL);
>   -
>   -    apr_thread_mutex_unlock(allowed_globals.jobmutex);
>   -}
>   -
>   -static int remove_job(void)
>   -{
>   -    joblist *job;
>   -    int sock;
>   -
>   -    WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
>   -    apr_thread_mutex_lock(allowed_globals.jobmutex);
>   -
>   -    if (shutdown_in_progress && !allowed_globals.jobhead) {
>   -        apr_thread_mutex_unlock(allowed_globals.jobmutex);
>   -	return (-1);
>   -    }
>   -    job = allowed_globals.jobhead;
>   -    ap_assert(job);
>   -    allowed_globals.jobhead = job->next;
>   -    if (allowed_globals.jobhead == NULL)
>   -	allowed_globals.jobtail = NULL;
>   -    apr_thread_mutex_unlock(allowed_globals.jobmutex);
>   -    sock = job->sock;
>   -    free(job);
>   -
>   -    return (sock);
>   -}
>   -
>   -static void win9x_accept(void * dummy)
>   -{
>   -    struct timeval tv;
>   -    fd_set main_fds;
>   -    int wait_time = 1;
>   -    int csd;
>   -    SOCKET nsd = INVALID_SOCKET;
>   -    struct sockaddr_in sa_client;
>   -    int count_select_errors = 0;
>   -    int rc;
>   -    int clen;
>   -    ap_listen_rec *lr;
>   -    struct fd_set listenfds;
>   -    SOCKET listenmaxfd = INVALID_SOCKET;
>   -
>   -    /* Setup the listeners
>   -     * ToDo: Use apr_poll()
>   -     */
>   -    FD_ZERO(&listenfds);
>   -    for (lr = ap_listeners; lr; lr = lr->next) {
>   -        if (lr->sd != NULL) {
>   -            apr_os_sock_get(&nsd, lr->sd);
>   -            FD_SET(nsd, &listenfds);
>   -            if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) {
>   -                listenmaxfd = nsd;
>   -            }
>   -        }
>   -    }
>   -    head_listener = ap_listeners;
>   -
>   -    while (!shutdown_in_progress) {
>   -	tv.tv_sec = wait_time;
>   -	tv.tv_usec = 0;
>   -	memcpy(&main_fds, &listenfds, sizeof(fd_set));
>   -
>   -	rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
>   -
>   -        if (rc == 0 || (rc == SOCKET_ERROR &&
> APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
>   -            count_select_errors = 0;    /* reset count of
> errors */
>   -            continue;
>   -        }
>   -        else if (rc == SOCKET_ERROR) {
>   -            /* A "real" error occurred, log it and increment
> the count of
>   -             * select errors. This count is used to ensure we
> don't go into
>   -             * a busy loop of continuous errors.
>   -             */
>   -            ap_log_error(APLOG_MARK, APLOG_INFO,
> apr_get_netos_error(), ap_server_conf,
>   -                         "select failed with error %d",
> apr_get_netos_error());
>   -            count_select_errors++;
>   -            if (count_select_errors > MAX_SELECT_ERRORS) {
>   -                shutdown_in_progress = 1;
>   -                ap_log_error(APLOG_MARK, APLOG_ERR,
> apr_get_netos_error(), ap_server_conf,
>   -                             "Too many errors in select loop.
> Child process exiting.");
>   -                break;
>   -            }
>   -	} else {
>   -	    ap_listen_rec *lr;
>   -
>   -	    lr = find_ready_listener(&main_fds);
>   -	    if (lr != NULL) {
>   -                /* fetch the native socket descriptor */
>   -                apr_os_sock_get(&nsd, lr->sd);
>   -	    }
>   -	}
>   -
>   -	do {
>   -            clen = sizeof(sa_client);
>   -            csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
>   -            if (csd == INVALID_SOCKET) {
>   -                csd = -1;
>   -            }
>   -        } while (csd < 0 &&
> APR_STATUS_IS_EINTR(apr_get_netos_error()));
>   -
>   -	if (csd < 0) {
>   -            if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
>   -		ap_log_error(APLOG_MARK, APLOG_ERR,
> apr_get_netos_error(), ap_server_conf,
>   -			    "accept: (client socket)");
>   -            }
>   -	}
>   -	else {
>   -	    add_job(csd);
>   -	}
>   -    }
>   -    SetEvent(exit_event);
>   -}
>   -
>   -static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
>   -{
>   -    apr_os_sock_info_t sockinfo;
>   -    int len;
>   -
>   -    if (context == NULL) {
>   -        /* allocate the completion context and the transaction pool */
>   -        context = apr_pcalloc(pconf, sizeof(COMP_CONTEXT));
>   -        apr_pool_create(&context->ptrans, pchild);
>   -        apr_pool_tag(context->ptrans, "ptrans");
>   -        context->ba = apr_bucket_alloc_create(pchild);
>   -    }
>   -
>   -    while (1) {
>   -        apr_pool_clear(context->ptrans);
>   -        context->accept_socket = remove_job();
>   -        if (context->accept_socket == -1) {
>   -            return NULL;
>   -        }
>   -	len = sizeof(struct sockaddr);
>   -        context->sa_server = apr_palloc(context->ptrans, len);
>   -        if (getsockname(context->accept_socket,
>   -                        context->sa_server, &len)== SOCKET_ERROR) {
>   -            ap_log_error(APLOG_MARK, APLOG_WARNING,
> apr_get_netos_error(), ap_server_conf,
>   -                         "getsockname failed");
>   -            continue;
>   -        }
>   -        len = sizeof(struct sockaddr);
>   -        context->sa_client = apr_palloc(context->ptrans, len);
>   -        if ((getpeername(context->accept_socket,
>   -                         context->sa_client, &len)) == SOCKET_ERROR) {
>   -            ap_log_error(APLOG_MARK, APLOG_WARNING,
> apr_get_netos_error(), ap_server_conf,
>   -                         "getpeername failed");
>   -            memset(&context->sa_client, '\0',
> sizeof(context->sa_client));
>   -        }
>   -        sockinfo.os_sock = &context->accept_socket;
>   -        sockinfo.local   = context->sa_server;
>   -        sockinfo.remote  = context->sa_client;
>   -        sockinfo.family  = APR_INET;
>   -        sockinfo.type    = SOCK_STREAM;
>   -        apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
>   -
>   -        return context;
>   -    }
>   -}
>   -/* Windows NT/2000 specific code...
>   - * Accept processing for on Windows NT uses a producer/consumer queue
>   - * model. An accept thread accepts connections off the network
> then issues
>   - * PostQueuedCompletionStatus() to awake a thread blocked on
> the ThreadDispatch
>   - * IOCompletionPort.
>   - *
>   - * winnt_accept()
>   - *    One or more accept threads run in this function, each of
> which accepts
>   - *    connections off the network and calls
> PostQueuedCompletionStatus() to
>   - *    queue an io completion packet to the ThreadDispatch
> IOCompletionPort.
>   - * winnt_get_connection()
>   - *    Worker threads block on the ThreadDispatch
> IOCompletionPort awaiting
>   - *    connections to service.
>   - */
>   -static void winnt_accept(void *lr_)
>   -{
>   -    ap_listen_rec *lr = (ap_listen_rec *)lr_;
>   -    apr_os_sock_info_t sockinfo;
>   -    PCOMP_CONTEXT context = NULL;
>   -    DWORD BytesRead;
>   -    SOCKET nlsd;
>   -    int rv;
>   -
>   -    apr_os_sock_get(&nlsd, lr->sd);
>   -
>   -    while (!shutdown_in_progress) {
>   -        if (!context) {
>   -            context = mpm_get_completion_context();
>   -            if (!context) {
>   -                /* Temporary resource constraint? */
>   -                Sleep(0);
>   -                continue;
>   -            }
>   -        }
>   -
>   -        /* Create and initialize the accept socket */
>   -        if (context->accept_socket == INVALID_SOCKET) {
>   -            context->accept_socket = socket(AF_INET,
> SOCK_STREAM, IPPROTO_TCP);
>   -            if (context->accept_socket == INVALID_SOCKET) {
>   -                /* Another temporary condition? */
>   -                ap_log_error(APLOG_MARK,APLOG_WARNING,
> apr_get_netos_error(), ap_server_conf,
>   -                             "winnt_accept: Failed to allocate
> an accept socket. "
>   -                             "Temporary resource constraint?
> Try again.");
>   -                Sleep(100);
>   -                continue;
>   -            }
>   -        }
>   -
>   -        /* AcceptEx on the completion context. The completion
> context will be
>   -         * signaled when a connection is accepted.
>   -         */
>   -        if (!AcceptEx(nlsd, context->accept_socket,
>   -                      context->buff,
>   -                      0,
>   -                      PADDED_ADDR_SIZE,
>   -                      PADDED_ADDR_SIZE,
>   -                      &BytesRead,
>   -                      &context->Overlapped)) {
>   -            rv = apr_get_netos_error();
>   -            if (rv == APR_FROM_OS_ERROR(WSAEINVAL)) {
>   -                /* Hack alert. Occasionally, TransmitFile will
> not recycle the
>   -                 * accept socket (usually when the client
> disconnects early).
>   -                 * Get a new socket and try the call again.
>   -                 */
>   -                closesocket(context->accept_socket);
>   -                context->accept_socket = INVALID_SOCKET;
>   -                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
> ap_server_conf,
>   -                       "winnt_accept: AcceptEx failed due to
> early client "
>   -                       "disconnect. Reallocate the accept
> socket and try again.");
>   -                continue;
>   -            }
>   -            else if (rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
>   -                ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
>   -                             "winnt_accept: AcceptEx failed.
> Attempting to recover.");
>   -                closesocket(context->accept_socket);
>   -                context->accept_socket = INVALID_SOCKET;
>   -                Sleep(100);
>   -                continue;
>   -            }
>   -
>   -            /* Wait for pending i/o. Wake up once per second
> to check for shutdown */
>   -            while (1) {
>   -                rv =
> WaitForSingleObject(context->Overlapped.hEvent, 1000);
>   -                if (rv == WAIT_OBJECT_0) {
>   -                    if
> (!GetOverlappedResult(context->Overlapped.hEvent,
>   -                                             &context->Overlapped,
>   -                                             &BytesRead, FALSE)) {
>   -                        ap_log_error(APLOG_MARK,APLOG_WARNING,
> GetLastError(), ap_server_conf,
>   -                                     "winnt_accept:
> Asynchronous AcceptEx failed.");
>   -                        closesocket(context->accept_socket);
>   -                        context->accept_socket = INVALID_SOCKET;
>   -                    }
>   -                    break;
>   -                }
>   -                /* WAIT_TIMEOUT */
>   -                if (shutdown_in_progress) {
>   -                    closesocket(context->accept_socket);
>   -                    context->accept_socket = INVALID_SOCKET;
>   -                    break;
>   -                }
>   -            }
>   -            if (context->accept_socket == INVALID_SOCKET) {
>   -                continue;
>   -            }
>   -        }
>   -
>   -        /* Inherit the listen socket settings. Required for
>   -         * shutdown() to work
>   -         */
>   -        if (setsockopt(context->accept_socket, SOL_SOCKET,
>   -                       SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
>   -                       sizeof(nlsd))) {
>   -            ap_log_error(APLOG_MARK, APLOG_WARNING,
> apr_get_netos_error(), ap_server_conf,
>   -                         "setsockopt(SO_UPDATE_ACCEPT_CONTEXT)
> failed.");
>   -            /* Not a failure condition. Keep running. */
>   -        }
>   -
>   -        /* Get the local & remote address */
>   -        GetAcceptExSockaddrs(context->buff,
>   -                             0,
>   -                             PADDED_ADDR_SIZE,
>   -                             PADDED_ADDR_SIZE,
>   -                             &context->sa_server,
>   -                             &context->sa_server_len,
>   -                             &context->sa_client,
>   -                             &context->sa_client_len);
>   -
>   -        sockinfo.os_sock = &context->accept_socket;
>   -        sockinfo.local   = context->sa_server;
>   -        sockinfo.remote  = context->sa_client;
>   -        sockinfo.family  = APR_INET;
>   -        sockinfo.type    = SOCK_STREAM;
>   -        apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
>   -
>   -        /* When a connection is received, send an io
> completion notification to
>   -         * the ThreadDispatchIOCP. This function could be replaced by
>   -         * mpm_post_completion_context(), but why do an extra
> function call...
>   -         */
>   -        PostQueuedCompletionStatus(ThreadDispatchIOCP, 0,
> IOCP_CONNECTION_ACCEPTED,
>   -                                   &context->Overlapped);
>   -        context = NULL;
>   -    }
>   -    if (!shutdown_in_progress) {
>   -        /* Yow, hit an irrecoverable error! Tell the child to die. */
>   -        SetEvent(exit_event);
>   -    }
>   -    ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: Accept thread exiting.", my_pid);
>   -}
>   -static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
>   -{
>   -    int rc;
>   -    DWORD BytesRead;
>   -    DWORD CompKey;
>   -    LPOVERLAPPED pol;
>   -
>   -    mpm_recycle_completion_context(context);
>   -
>   -    apr_atomic_inc(&g_blocked_threads);
>   -    while (1) {
>   -        if (workers_may_exit) {
>   -            apr_atomic_dec(&g_blocked_threads);
>   -            return NULL;
>   -        }
>   -        rc = GetQueuedCompletionStatus(ThreadDispatchIOCP,
> &BytesRead, &CompKey,
>   -                                       &pol, INFINITE);
>   -        if (!rc) {
>   -            rc = apr_get_os_error();
>   -            ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
>   -                             "Child %d:
> GetQueuedComplationStatus returned %d", my_pid, rc);
>   -            continue;
>   -        }
>   -
>   -        switch (CompKey) {
>   -        case IOCP_CONNECTION_ACCEPTED:
>   -            context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
>   -            break;
>   -        case IOCP_SHUTDOWN:
>   -            apr_atomic_dec(&g_blocked_threads);
>   -            return NULL;
>   -        default:
>   -            apr_atomic_dec(&g_blocked_threads);
>   -            return NULL;
>   -        }
>   -        break;
>   -    }
>   -    apr_atomic_dec(&g_blocked_threads);
>   -
>   -    return context;
>   -}
>   -
>   -/*
>   - * worker_main()
>   - * Main entry point for the worker threads. Worker threads block in
>   - * win*_get_connection() awaiting a connection to service.
>   - */
>   -static void worker_main(long thread_num)
>   -{
>   -    static int requests_this_child = 0;
>   -    PCOMP_CONTEXT context = NULL;
>   -    ap_sb_handle_t *sbh;
>   -
>   -    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: Worker thread %d starting.",
> my_pid, thread_num);
>   -    while (1) {
>   -        conn_rec *c;
>   -        apr_int32_t disconnected;
>   -
>   -        ap_update_child_status_from_indexes(0, thread_num,
> SERVER_READY, NULL);
>   -
>   -        /* Grab a connection off the network */
>   -        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
>   -            context = win9x_get_connection(context);
>   -        }
>   -        else {
>   -            context = winnt_get_connection(context);
>   -        }
>   -        if (!context) {
>   -            /* Time for the thread to exit */
>   -            break;
>   -        }
>   -
>   -        /* Have we hit MaxRequestPerChild connections? */
>   -        if (ap_max_requests_per_child) {
>   -            requests_this_child++;
>   -            if (requests_this_child > ap_max_requests_per_child) {
>   -                SetEvent(max_requests_per_child_event);
>   -            }
>   -        }
>   -
>   -        ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
>   -        c = ap_run_create_connection(context->ptrans, ap_server_conf,
>   -                                     context->sock, thread_num, sbh,
>   -                                     context->ba);
>   -
>   -        if (c) {
>   -            ap_process_connection(c, context->sock);
>   -            apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
>   -                               &disconnected);
>   -            if (!disconnected) {
>   -                context->accept_socket = INVALID_SOCKET;
>   -                ap_lingering_close(c);
>   -            }
>   -        }
>   -        else {
>   -            /* ap_run_create_connection closes the socket on failure */
>   -            context->accept_socket = INVALID_SOCKET;
>   -        }
>   -    }
>   -
>   -    ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD,
>   -                                        (request_rec *) NULL);
>   -
>   -    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: Worker thread %d exiting.",
> my_pid, thread_num);
>   -}
>   -
>   -static void cleanup_thread(thread *handles, int *thread_cnt,
> int thread_to_clean)
>   -{
>   -    int i;
>   -
>   -    CloseHandle(handles[thread_to_clean]);
>   -    for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
>   -	handles[i] = handles[i + 1];
>   -    (*thread_cnt)--;
>   -}
>   -
>   -/*
>   - * child_main()
>   - * Entry point for the main control thread for the child process.
>   - * This thread creates the accept thread, worker threads and
>   - * monitors the child process for maintenance and shutdown
>   - * events.
>   - */
>   -static void create_listener_thread()
>   -{
>   -    int tid;
>   -    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
>   -        _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) win9x_accept,
>   -                       NULL, 0, &tid);
>   -    } else {
>   -        /* Start an accept thread per listener
>   -         * XXX: Why would we have a NULL sd in our listeners?
>   -         */
>   -        ap_listen_rec *lr;
>   -        for (lr = ap_listeners; lr; lr = lr->next) {
>   -            if (lr->sd != NULL) {
>   -                _beginthreadex(NULL, 1000,
> (LPTHREAD_START_ROUTINE) winnt_accept,
>   -                               (void *) lr, 0, &tid);
>   -            }
>   -        }
>   -    }
>   -}
>   -static void child_main()
>   -{
>   -    apr_status_t status;
>   -    apr_hash_t *ht;
>   -    ap_listen_rec *lr;
>   -    HANDLE child_events[2];
>   -    int threads_created = 0;
>   -    int listener_started = 0;
>   -    int tid;
>   -    thread *child_handles;
>   -    int rv;
>   -    time_t end_time;
>   -    int i;
>   -    int cld;
>   -
>   -    apr_pool_create(&pchild, pconf);
>   -    apr_pool_tag(pchild, "pchild");
>   -
>   -    ap_run_child_init(pchild, ap_server_conf);
>   -    ht = apr_hash_make(pchild);
>   -
>   -    /* Initialize the child_events */
>   -    max_requests_per_child_event = CreateEvent(NULL, TRUE,
> FALSE, NULL);
>   -    if (!max_requests_per_child_event) {
>   -        ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_os_error(), ap_server_conf,
>   -                     "Child %d: Failed to create a
> max_requests event.", my_pid);
>   -        exit(APEXIT_CHILDINIT);
>   -    }
>   -    child_events[0] = exit_event;
>   -    child_events[1] = max_requests_per_child_event;
>   -
>   -    allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0,
> 1000000, NULL);
>   -    apr_thread_mutex_create(&allowed_globals.jobmutex,
>   -                            APR_THREAD_MUTEX_DEFAULT, pchild);
>   -
>   -    /*
>   -     * Wait until we have permission to start accepting connections.
>   -     * start_mutex is used to ensure that only one child ever
>   -     * goes into the listen/accept loop at once.
>   -     */
>   -    status = apr_proc_mutex_lock(start_mutex);
>   -    if (status != APR_SUCCESS) {
>   -        ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
>   -                     "Child %d: Failed to acquire the
> start_mutex. Process will exit.", my_pid);
>   -        exit(APEXIT_CHILDINIT);
>   -    }
>   -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: Acquired the start mutex.", my_pid);
>   -
>   -    /*
>   -     * Create the worker thread dispatch IOCompletionPort
>   -     * on Windows NT/2000
>   -     */
>   -    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
>   -        /* Create the worker thread dispatch IOCP */
>   -        ThreadDispatchIOCP =
> CreateIoCompletionPort(INVALID_HANDLE_VALUE,
>   -                                                    NULL,
>   -                                                    0,
>   -                                                    0); /*
> CONCURRENT ACTIVE THREADS */
>   -        apr_thread_mutex_create(&qlock,
> APR_THREAD_MUTEX_DEFAULT, pchild);
>   -    }
>   -
>   -    /*
>   -     * Create the pool of worker threads
>   -     */
>   -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: Starting %d worker threads.",
> my_pid, ap_threads_per_child);
>   -    child_handles = (thread) apr_pcalloc(pchild,
> ap_threads_per_child * sizeof(int));
>   -    while (1) {
>   -        for (i = 0; i < ap_threads_per_child; i++) {
>   -            int *score_idx;
>   -            int status = ap_scoreboard_image->servers[0][i].status;
>   -            if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
>   -                continue;
>   -            }
>   -            ap_update_child_status_from_indexes(0, i,
> SERVER_STARTING, NULL);
>   -            child_handles[i] = (thread) _beginthreadex(NULL,
> 0, (LPTHREAD_START_ROUTINE) worker_main,
>   -                                                       (void
> *) i, 0, &tid);
>   -            if (child_handles[i] == 0) {
>   -                ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_os_error(), ap_server_conf,
>   -                             "Child %d: _beginthreadex failed.
> Unable to create all worker threads. "
>   -                             "Created %d of the %d threads
> requested with the ThreadsPerChild configuration directive.",
>   -                             threads_created, ap_threads_per_child);
>   -                ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
>   -                goto shutdown;
>   -            }
>   -            threads_created++;
>   -            /* Save the score board index in ht keyed to the
> thread handle. We need this
>   -             * when cleaning up threads down below...
>   -             */
>   -            score_idx = apr_pcalloc(pchild, sizeof(int));
>   -            *score_idx = i;
>   -            apr_hash_set(ht, &child_handles[i],
> sizeof(thread), score_idx);
>   -        }
>   -        /* Start the listener only when workers are available */
>   -        if (!listener_started && threads_created) {
>   -            create_listener_thread();
>   -            listener_started = 1;
>   -        }
>   -        if (threads_created == ap_threads_per_child) {
>   -            break;
>   -        }
>   -        /* Check to see if the child has been told to exit */
>   -        if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
>   -            break;
>   -        }
>   -        /* wait for previous generation to clean up an entry
> in the scoreboard */
>   -        apr_sleep(1 * APR_USEC_PER_SEC);
>   -    }
>   -
>   -    /* Wait for one of three events:
>   -     * exit_event:
>   -     *    The exit_event is signaled by the parent process to notify
>   -     *    the child that it is time to exit.
>   -     *
>   -     * max_requests_per_child_event:
>   -     *    This event is signaled by the worker threads to indicate that
>   -     *    the process has handled MaxRequestsPerChild connections.
>   -     *
>   -     * TIMEOUT:
>   -     *    To do periodic maintenance on the server (check for
> thread exits,
>   -     *    number of completion contexts, etc.)
>   -     */
>   -    while (1) {
>   -        rv = WaitForMultipleObjects(2, (HANDLE *)
> child_events, FALSE, 1000);
>   -        cld = rv - WAIT_OBJECT_0;
>   -        if (rv == WAIT_FAILED) {
>   -            /* Something serious is wrong */
>   -            ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_os_error(), ap_server_conf,
>   -                         "Child %d: WAIT_FAILED -- shutting
> down server");
>   -            break;
>   -        }
>   -        else if (rv == WAIT_TIMEOUT) {
>   -            apr_proc_other_child_check();
>   -        }
>   -        else if (cld == 0) {
>   -            /* Exit event was signaled */
>   -            ap_log_error(APLOG_MARK, APLOG_NOTICE,
> APR_SUCCESS, ap_server_conf,
>   -                         "Child %d: Exit event signaled. Child
> process is ending.", my_pid);
>   -            break;
>   -        }
>   -        else {
>   -            /* MaxRequestsPerChild event set by the worker threads.
>   -             * Signal the parent to restart
>   -             */
>   -            ap_log_error(APLOG_MARK, APLOG_NOTICE,
> APR_SUCCESS, ap_server_conf,
>   -                         "Child %d: Process exiting because it
> reached "
>   -                         "MaxRequestsPerChild. Signaling the
> parent to "
>   -                         "restart a new child process.", my_pid);
>   -            ap_signal_parent(SIGNAL_PARENT_RESTART);
>   -            break;
>   -        }
>   -    }
>   -
>   -    /*
>   -     * Time to shutdown the child process
>   -     */
>   -
>   - shutdown:
>   -    /* Setting is_graceful will cause threads handling
> keep-alive connections
>   -     * to close the connection after handling the current request.
>   -     */
>   -    is_graceful = 1;
>   -
>   -    /* Close the listening sockets. Note, we must close the listeners
>   -     * before closing any accept sockets pending in AcceptEx to prevent
>   -     * memory leaks in the kernel.
>   -     */
>   -    for (lr = ap_listeners; lr ; lr = lr->next) {
>   -        apr_socket_close(lr->sd);
>   -    }
>   -
>   -    /* Shutdown listener threads and pending AcceptEx socksts
>   -     * but allow the worker threads to continue consuming from
>   -     * the queue of accepted connections.
>   -     */
>   -    shutdown_in_progress = 1;
>   -
>   -    Sleep(1000);
>   -
>   -    /* Tell the worker threads to exit */
>   -    workers_may_exit = 1;
>   -
>   -    /* Release the start_mutex to let the new process (in the restart
>   -     * scenario) a chance to begin accepting and servicing requests
>   -     */
>   -    rv = apr_proc_mutex_unlock(start_mutex);
>   -    if (rv == APR_SUCCESS) {
>   -        ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf,
>   -                     "Child %d: Released the start mutex", my_pid);
>   -    }
>   -    else {
>   -        ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
>   -                     "Child %d: Failure releasing the start
> mutex", my_pid);
>   -    }
>   -
>   -    /* Shutdown the worker threads */
>   -    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
>   -        for (i = 0; i < threads_created; i++) {
>   -            add_job(-1);
>   -        }
>   -    }
>   -    else { /* Windows NT/2000 */
>   -        /* Post worker threads blocked on the ThreadDispatch
> IOCompletion port */
>   -        while (g_blocked_threads > 0) {
>   -            ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS,
> ap_server_conf,
>   -                         "Child %d: %d threads blocked on the
> completion port", my_pid, g_blocked_threads);
>   -            for (i=g_blocked_threads; i > 0; i--) {
>   -                PostQueuedCompletionStatus(ThreadDispatchIOCP,
> 0, IOCP_SHUTDOWN, NULL);
>   -            }
>   -            Sleep(1000);
>   -        }
>   -        /* Empty the accept queue of completion contexts */
>   -        apr_thread_mutex_lock(qlock);
>   -        while (qhead) {
>   -            CloseHandle(qhead->Overlapped.hEvent);
>   -            closesocket(qhead->accept_socket);
>   -            qhead = qhead->next;
>   -        }
>   -        apr_thread_mutex_unlock(qlock);
>   -    }
>   -
>   -    /* Give busy worker threads a chance to service their
> connections */
>   -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: Waiting for %d worker threads to
> exit.", my_pid, threads_created);
>   -    end_time = time(NULL) + 180;
>   -    while (threads_created) {
>   -        rv = wait_for_many_objects(threads_created,
> child_handles, end_time - time(NULL));
>   -        if (rv != WAIT_TIMEOUT) {
>   -            rv = rv - WAIT_OBJECT_0;
>   -            ap_assert((rv >= 0) && (rv < threads_created));
>   -            cleanup_thread(child_handles, &threads_created, rv);
>   -            continue;
>   -        }
>   -        break;
>   -    }
>   -
>   -    /* Kill remaining threads off the hard way */
>   -    if (threads_created) {
>   -        ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS,
> ap_server_conf,
>   -                     "Child %d: Terminating %d threads that
> failed to exit.", my_pid);
>   -    }
>   -    for (i = 0; i < threads_created; i++) {
>   -        int *score_idx;
>   -        TerminateThread(child_handles[i], 1);
>   -        CloseHandle(child_handles[i]);
>   -        /* Reset the scoreboard entry for the thread we just whacked */
>   -        score_idx = apr_hash_get(ht, &child_handles[i],
> sizeof(thread));
>   -        ap_update_child_status_from_indexes(0, *score_idx,
> SERVER_DEAD, NULL);
>   -    }
>   -    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
>   -                 "Child %d: All worker threads have exited.", my_pid);
>   -
>   -    CloseHandle(allowed_globals.jobsemaphore);
>   -    apr_thread_mutex_destroy(allowed_globals.jobmutex);
>   -    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
>   -    	apr_thread_mutex_destroy(qlock);
>   -
>   -    apr_pool_destroy(pchild);
>   -    CloseHandle(exit_event);
>   -}
>
>    static int send_handles_to_child(apr_pool_t *p,
>                                     HANDLE child_ready_event,
>   @@ -1542,6 +446,83 @@
>        return 0;
>    }
>
>   +
>   +/*
>   + * get_listeners_from_parent()
>   + * The listen sockets are opened in the parent. This function,
> which runs
>   + * exclusively in the child process, receives them from the parent and
>   + * makes them availeble in the child.
>   + */
>   +void get_listeners_from_parent(server_rec *s)
>   +{
>   +    WSAPROTOCOL_INFO WSAProtocolInfo;
>   +    HANDLE pipe;
>   +    ap_listen_rec *lr;
>   +    DWORD BytesRead;
>   +    int lcnt = 0;
>   +    SOCKET nsd;
>   +
>   +    /* Set up a default listener if necessary */
>   +    if (ap_listeners == NULL) {
>   +        ap_listen_rec *lr;
>   +        lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
>   +        lr->sd = NULL;
>   +        lr->next = ap_listeners;
>   +        ap_listeners = lr;
>   +    }
>   +
>   +    /* Open the pipe to the parent process to receive the
> inherited socket
>   +     * data. The sockets have been set to listening in the
> parent process.
>   +     */
>   +    pipe = GetStdHandle(STD_INPUT_HANDLE);
>   +
>   +    for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
>   +        if (!ReadFile(pipe, &WSAProtocolInfo,
> sizeof(WSAPROTOCOL_INFO),
>   +                      &BytesRead, (LPOVERLAPPED) NULL)) {
>   +            ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_os_error(), ap_server_conf,
>   +                         "setup_inherited_listeners: Unable to
> read socket data from parent");
>   +            exit(APEXIT_CHILDINIT);
>   +        }
>   +        nsd = WSASocket(FROM_PROTOCOL_INFO,
> FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
>   +                        &WSAProtocolInfo, 0, 0);
>   +        if (nsd == INVALID_SOCKET) {
>   +            ap_log_error(APLOG_MARK, APLOG_CRIT,
> apr_get_netos_error(), ap_server_conf,
>   +                         "Child %d:
> setup_inherited_listeners(), WSASocket failed to open the
> inherited socket.", my_pid);
>   +            exit(APEXIT_CHILDINIT);
>   +        }
>   +
>   +        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
>   +            HANDLE hProcess = GetCurrentProcess();
>   +            HANDLE dup;
>   +            if (DuplicateHandle(hProcess, (HANDLE) nsd,
> hProcess, &dup,
>   +                                0, FALSE, DUPLICATE_SAME_ACCESS)) {
>   +                closesocket(nsd);
>   +                nsd = (SOCKET) dup;
>   +            }
>   +        }
>   +        else {
>   +            /* A different approach.  Many users report errors such as
>   +             * (32538)An operation was attempted on something
> that is not
>   +             * a socket.  : Parent: WSADuplicateSocket failed...
>   +             *
>   +             * This appears that the duplicated handle is no
> longer recognized
>   +             * as a socket handle.  SetHandleInformation
> should overcome that
>   +             * problem by not altering the handle identifier.
> But this won't
>   +             * work on 9x - it's unsupported.
>   +             */
>   +            if (!SetHandleInformation((HANDLE)nsd,
> HANDLE_FLAG_INHERIT, 0)) {
>   +                ap_log_error(APLOG_MARK, APLOG_ERR,
> apr_get_os_error(), ap_server_conf,
>   +                             "set_listeners_noninheritable:
> SetHandleInformation failed.");
>   +            }
>   +        }
>   +        apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
>   +    }
>   +
>   +    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
>   +                 "Child %d: retrieved %d listeners from
> parent", my_pid, lcnt);
>   +}
>   +
>   +
>    static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId,
>                                       apr_file_t *child_in)
>    {
>   @@ -2508,10 +1489,6 @@
>            return DONE;
>        }
>
>   -    if (!set_listeners_noninheritable(s->process->pool)) {
>   -        return 1;
>   -    }
>   -
>        return OK;
>    }
>
>   @@ -2524,7 +1501,7 @@
>        /* This is a child process, not in single process mode */
>        if (!one_process) {
>            /* Set up events and the scoreboard */
>   -        get_handles_from_parent(s);
>   +        get_handles_from_parent(s, ap_scoreboard_shm);
>
>            /* Set up the listeners */
>            get_listeners_from_parent(s);
>   @@ -2573,7 +1550,7 @@
>            ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS,
> ap_server_conf,
>                         "Child %d: Child process is running", my_pid);
>
>   -        child_main();
>   +        child_main(pconf);
>
>            ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS,
> ap_server_conf,
>                         "Child %d: Child process is exiting",
> my_pid);
>   @@ -2631,3 +1608,5 @@
>        winnt_cmds,		        /* command apr_table_t */
>        winnt_hooks 		/* register_hooks */
>    };
>   +
>   +#endif /* def WIN32 */
>
>
>
>   1.39      +0 -43     httpd-2.0/server/mpm/winnt/mpm_winnt.h
>
>   Index: mpm_winnt.h
>   ===================================================================
>   RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.h,v
>   retrieving revision 1.38
>   retrieving revision 1.39
>   diff -u -r1.38 -r1.39
>   --- mpm_winnt.h	24 Jun 2002 07:53:50 -0000	1.38
>   +++ mpm_winnt.h	29 Jul 2002 05:12:50 -0000	1.39
>   @@ -113,49 +113,6 @@
>    } ap_signal_parent_e;
>    AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type);
>
>   -/* This code is stolen from the apr_private.h and misc/win32/misc.c
>   - * Please see those sources for detailed documentation.
>   - */
>   -typedef enum {
>   -    DLL_WINBASEAPI = 0,    // kernel32 From WinBase.h
>   -    DLL_WINADVAPI = 1,     // advapi32 From WinBase.h
>   -    DLL_WINSOCKAPI = 2,    // mswsock  From WinSock.h
>   -    DLL_WINSOCK2API = 3,   // ws2_32   From WinSock2.h
>   -    DLL_defined = 4        // must define as last idx_ + 1
>   -} ap_dlltoken_e;
>   -
>   -FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char *fnName,
> int ordinal);
>   -
>   -#define AP_DECLARE_LATE_DLL_FUNC(lib, rettype, calltype, fn,
> ord, args, names) \
>   -    typedef rettype (calltype *ap_winapi_fpt_##fn) args; \
>   -    static ap_winapi_fpt_##fn ap_winapi_pfn_##fn = NULL; \
>   -    __inline rettype ap_winapi_##fn args \
>   -    {   if (!ap_winapi_pfn_##fn) \
>   -            ap_winapi_pfn_##fn = (ap_winapi_fpt_##fn)
> ap_load_dll_func(lib, #fn, ord); \
>   -        return (*(ap_winapi_pfn_##fn)) names; }; \
>   -
>   -/* Win2K kernel only */
>   -AP_DECLARE_LATE_DLL_FUNC(DLL_WINADVAPI, BOOL, WINAPI,
> ChangeServiceConfig2A, 0, (
>   -    SC_HANDLE hService,
>   -    DWORD dwInfoLevel,
>   -    LPVOID lpInfo),
>   -    (hService, dwInfoLevel, lpInfo));
>   -#undef ChangeServiceConfig2
>   -#define ChangeServiceConfig2 ap_winapi_ChangeServiceConfig2A
>   -
>   -/* WinNT kernel only */
>   -AP_DECLARE_LATE_DLL_FUNC(DLL_WINBASEAPI, BOOL, WINAPI, CancelIo, 0, (
>   -    IN HANDLE hFile),
>   -    (hFile));
>   -#define CancelIo ap_winapi_CancelIo
>   -
>   -/* Win9x kernel only */
>   -AP_DECLARE_LATE_DLL_FUNC(DLL_WINBASEAPI, DWORD, WINAPI,
> RegisterServiceProcess, 0, (
>   -    DWORD dwProcessId,
>   -    DWORD dwType),
>   -    (dwProcessId, dwType));
>   -#define RegisterServiceProcess ap_winapi_RegisterServiceProcess
>   -
>    /*
>     * The Windoes MPM uses a queue of completion contexts that it passes
>     * between the accept threads and the worker threads. Declare the
>
>
>
>