upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / server / mpm / winnt / child.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifdef WIN32
18
19 #define CORE_PRIVATE 
20 #include "httpd.h" 
21 #include "http_main.h" 
22 #include "http_log.h" 
23 #include "http_config.h"        /* for read_config */ 
24 #include "http_core.h"          /* for get_remote_host */ 
25 #include "http_connection.h"
26 #include "apr_portable.h"
27 #include "apr_thread_proc.h"
28 #include "apr_getopt.h"
29 #include "apr_strings.h"
30 #include "apr_lib.h"
31 #include "apr_shm.h"
32 #include "apr_thread_mutex.h"
33 #include "ap_mpm.h"
34 #include "ap_config.h"
35 #include "ap_listen.h"
36 #include "mpm_default.h"
37 #include "mpm_winnt.h"
38 #include "mpm_common.h"
39 #include <malloc.h>
40 #include "apr_atomic.h"
41
42 /* shared with mpm_winnt.c */
43 extern DWORD my_pid;
44
45 /* used by parent to signal the child to start and exit */
46 /* shared with mpm_winnt.c, but should be private to child.c */
47 apr_proc_mutex_t *start_mutex;
48 HANDLE exit_event;
49
50 /* child_main() should never need to modify is_graceful!?! */
51 extern int volatile is_graceful;
52
53 /* Queue for managing the passing of COMP_CONTEXTs between
54  * the accept and worker threads.
55  */
56 static apr_pool_t *pchild;
57 static int shutdown_in_progress = 0;
58 static int workers_may_exit = 0;
59 static unsigned int g_blocked_threads = 0;
60 static HANDLE max_requests_per_child_event;
61
62 static apr_thread_mutex_t  *child_lock;
63 static apr_thread_mutex_t  *qlock;
64 static PCOMP_CONTEXT qhead = NULL;
65 static PCOMP_CONTEXT qtail = NULL;
66 static int num_completion_contexts = 0;
67 static int max_num_completion_contexts = 0;
68 static HANDLE ThreadDispatchIOCP = NULL;
69 static HANDLE qwait_event = NULL;
70
71
72 AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context)
73 {
74     /* Recycle the completion context.
75      * - clear the ptrans pool
76      * - put the context on the queue to be consumed by the accept thread
77      * Note: 
78      * context->accept_socket may be in a disconnected but reusable 
79      * state so -don't- close it.
80      */
81     if (context) {
82         apr_pool_clear(context->ptrans);
83         context->ba = apr_bucket_alloc_create(context->ptrans);
84         context->next = NULL;
85         ResetEvent(context->Overlapped.hEvent);
86         apr_thread_mutex_lock(qlock);
87         if (qtail) {
88             qtail->next = context;
89         } else {
90             qhead = context;
91             SetEvent(qwait_event);
92         }
93         qtail = context;
94         apr_thread_mutex_unlock(qlock);
95     }
96 }
97
98 AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void)
99 {
100     apr_status_t rv;
101     PCOMP_CONTEXT context = NULL;
102
103     while (1) {
104         /* Grab a context off the queue */
105         apr_thread_mutex_lock(qlock);
106         if (qhead) {
107             context = qhead;
108             qhead = qhead->next;
109             if (!qhead)
110                 qtail = NULL;
111         } else {
112             ResetEvent(qwait_event);
113         }
114         apr_thread_mutex_unlock(qlock);
115   
116         if (!context) {
117             /* We failed to grab a context off the queue, consider allocating
118              * a new one out of the child pool. There may be up to 
119              * (ap_threads_per_child + num_listeners) contexts in the system 
120              * at once.
121              */
122             if (num_completion_contexts >= max_num_completion_contexts) {
123                 /* All workers are busy, need to wait for one */
124                 static int reported = 0;
125                 if (!reported) {
126                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
127                                  "Server ran out of threads to serve requests. Consider "
128                                  "raising the ThreadsPerChild setting");
129                     reported = 1;
130                 }
131
132                 /* Wait for a worker to free a context. Once per second, give
133                  * the caller a chance to check for shutdown. If the wait
134                  * succeeds, get the context off the queue. It must be available,
135                  * since there's only one consumer.
136                  */
137                 rv = WaitForSingleObject(qwait_event, 1000);
138                 if (rv == WAIT_OBJECT_0)
139                     continue;
140                 else /* Hopefully, WAIT_TIMEOUT */
141                     return NULL;
142             } else {
143                 /* Allocate another context.
144                  * Note:
145                  * Multiple failures in the next two steps will cause the pchild pool
146                  * to 'leak' storage. I don't think this is worth fixing...
147                  */
148                 apr_allocator_t *allocator;
149
150                 apr_thread_mutex_lock(child_lock);
151                 context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
152   
153                 context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
154                 if (context->Overlapped.hEvent == NULL) {
155                     /* Hopefully this is a temporary condition ... */
156                     ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
157                                  "mpm_get_completion_context: CreateEvent failed.");
158
159                     apr_thread_mutex_unlock(child_lock);
160                     return NULL;
161                 }
162  
163                 /* Create the tranaction pool */
164                 apr_allocator_create(&allocator);
165                 apr_allocator_max_free_set(allocator, ap_max_mem_free);
166                 rv = apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
167                 if (rv != APR_SUCCESS) {
168                     ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
169                                  "mpm_get_completion_context: Failed to create the transaction pool.");
170                     CloseHandle(context->Overlapped.hEvent);
171
172                     apr_thread_mutex_unlock(child_lock);
173                     return NULL;
174                 }
175                 apr_allocator_owner_set(allocator, context->ptrans);
176                 apr_pool_tag(context->ptrans, "transaction");
177                 context->accept_socket = INVALID_SOCKET;
178                 context->ba = apr_bucket_alloc_create(context->ptrans);
179                 apr_atomic_inc(&num_completion_contexts); 
180
181                 apr_thread_mutex_unlock(child_lock);
182                 break;
183             }
184         } else {
185             /* Got a context from the queue */
186             break;
187         }
188     }
189
190     return context;
191 }
192
193 AP_DECLARE(apr_status_t) mpm_post_completion_context(PCOMP_CONTEXT context, 
194                                                      io_state_e state)
195 {
196     LPOVERLAPPED pOverlapped;
197     if (context)
198         pOverlapped = &context->Overlapped;
199     else
200         pOverlapped = NULL;
201
202     PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped);
203     return APR_SUCCESS;
204 }
205
206
207 /*
208  * find_ready_listener()
209  * Only used by Win9* and should go away when the win9*_accept() function is 
210  * reimplemented using apr_poll().
211  */
212 static ap_listen_rec *head_listener;
213
214 static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
215 {
216     ap_listen_rec *lr;
217     SOCKET nsd;
218
219     lr = head_listener;
220     do {
221         apr_os_sock_get(&nsd, lr->sd);
222         if (FD_ISSET(nsd, main_fds)) {
223             head_listener = lr->next;
224             if (!head_listener) {
225                 head_listener = ap_listeners;
226             }
227             return lr;
228         }
229         lr = lr->next;
230         if (!lr) {
231             lr = ap_listeners;
232         }
233     } while (lr != head_listener);
234     return NULL;
235 }
236
237
238 /* Windows 9x specific code...
239  * Accept processing for on Windows 95/98 uses a producer/consumer queue 
240  * model. A single thread accepts connections and queues the accepted socket 
241  * to the accept queue for consumption by a pool of worker threads.
242  *
243  * win9x_accept()
244  *    The accept threads runs this function, which accepts connections off 
245  *    the network and calls add_job() to queue jobs to the accept_queue.
246  * add_job()/remove_job()
247  *    Add or remove an accepted socket from the list of sockets 
248  *    connected to clients. allowed_globals.jobmutex protects
249  *    against multiple concurrent access to the linked list of jobs.
250  * win9x_get_connection()
251  *    Calls remove_job() to pull a job from the accept queue. All the worker 
252  *    threads block on remove_job.
253  */
254
255 typedef struct joblist_s {
256     struct joblist_s *next;
257     int sock;
258 } joblist;
259
260 typedef struct globals_s {
261     HANDLE jobsemaphore;
262     joblist *jobhead;
263     joblist *jobtail;
264     apr_thread_mutex_t *jobmutex;
265     int jobcount;
266 } globals;
267
268 globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
269
270 #define MAX_SELECT_ERRORS 100
271
272
273 static void add_job(int sock)
274 {
275     joblist *new_job;
276
277     new_job = (joblist *) malloc(sizeof(joblist));
278     if (new_job == NULL) {
279         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
280                      "Ouch!  Out of memory in add_job()!");
281         return;
282     }
283     new_job->next = NULL;
284     new_job->sock = sock;
285
286     apr_thread_mutex_lock(allowed_globals.jobmutex);
287
288     if (allowed_globals.jobtail != NULL)
289         allowed_globals.jobtail->next = new_job;
290     allowed_globals.jobtail = new_job;
291     if (!allowed_globals.jobhead)
292         allowed_globals.jobhead = new_job;
293     allowed_globals.jobcount++;
294     ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL);
295
296     apr_thread_mutex_unlock(allowed_globals.jobmutex);
297 }
298
299
300 static int remove_job(void)
301 {
302     joblist *job;
303     int sock;
304
305     WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
306     apr_thread_mutex_lock(allowed_globals.jobmutex);
307
308     if (shutdown_in_progress && !allowed_globals.jobhead) {
309         apr_thread_mutex_unlock(allowed_globals.jobmutex);
310         return (INVALID_SOCKET);
311     }
312     job = allowed_globals.jobhead;
313     ap_assert(job);
314     allowed_globals.jobhead = job->next;
315     if (allowed_globals.jobhead == NULL)
316         allowed_globals.jobtail = NULL;
317     apr_thread_mutex_unlock(allowed_globals.jobmutex);
318     sock = job->sock;
319     free(job);
320
321     return (sock);
322 }
323
324
325 static unsigned int __stdcall win9x_accept(void * dummy)
326 {
327     struct timeval tv;
328     fd_set main_fds;
329     int wait_time = 1;
330     int csd;
331     SOCKET nsd = INVALID_SOCKET;
332     struct sockaddr_in sa_client;
333     int count_select_errors = 0;
334     int rc;
335     int clen;
336     ap_listen_rec *lr;
337     struct fd_set listenfds;
338     SOCKET listenmaxfd = INVALID_SOCKET;
339
340     /* Setup the listeners 
341      * ToDo: Use apr_poll()
342      */
343     FD_ZERO(&listenfds);
344     for (lr = ap_listeners; lr; lr = lr->next) {
345         if (lr->sd != NULL) {
346             apr_os_sock_get(&nsd, lr->sd);
347             FD_SET(nsd, &listenfds);
348             if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) {
349                 listenmaxfd = nsd;
350             }
351             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
352                          "Child %d: Listening on port %d.", my_pid, lr->bind_addr->port);
353         }
354     }
355
356     head_listener = ap_listeners;
357
358     while (!shutdown_in_progress) {
359         tv.tv_sec = wait_time;
360         tv.tv_usec = 0;
361         memcpy(&main_fds, &listenfds, sizeof(fd_set));
362
363         rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
364
365         if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
366             count_select_errors = 0;    /* reset count of errors */            
367             continue;
368         }
369         else if (rc == SOCKET_ERROR) {
370             /* A "real" error occurred, log it and increment the count of
371              * select errors. This count is used to ensure we don't go into
372              * a busy loop of continuous errors.
373              */
374             ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf, 
375                          "select failed with error %d", apr_get_netos_error());
376             count_select_errors++;
377             if (count_select_errors > MAX_SELECT_ERRORS) {
378                 shutdown_in_progress = 1;
379                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
380                              "Too many errors in select loop. Child process exiting.");
381                 break;
382             }
383         } else {
384             ap_listen_rec *lr;
385
386             lr = find_ready_listener(&main_fds);
387             if (lr != NULL) {
388                 /* fetch the native socket descriptor */
389                 apr_os_sock_get(&nsd, lr->sd);
390             }
391         }
392
393         do {
394             clen = sizeof(sa_client);
395             csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
396         } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
397
398         if (csd < 0) {
399             if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
400                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
401                             "accept: (client socket)");
402             }
403         }
404         else {
405             add_job(csd);
406         }
407     }
408     SetEvent(exit_event);
409     return 0;
410 }
411
412
413 static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
414 {
415     apr_os_sock_info_t sockinfo;
416     int len;
417
418     if (context == NULL) {
419         /* allocate the completion context and the transaction pool */
420         apr_allocator_t *allocator;
421         apr_thread_mutex_lock(child_lock);
422         context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
423         apr_allocator_create(&allocator);
424         apr_allocator_max_free_set(allocator, ap_max_mem_free);
425         apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
426         apr_allocator_owner_set(allocator, context->ptrans);
427         apr_pool_tag(context->ptrans, "transaction");
428         apr_thread_mutex_unlock(child_lock);
429     }
430     
431     while (1) {
432         apr_pool_clear(context->ptrans);        
433         context->ba = apr_bucket_alloc_create(context->ptrans);
434         context->accept_socket = remove_job();
435         if (context->accept_socket == INVALID_SOCKET) {
436             return NULL;
437         }
438         len = sizeof(struct sockaddr);
439         context->sa_server = apr_palloc(context->ptrans, len);
440         if (getsockname(context->accept_socket, 
441                         context->sa_server, &len)== SOCKET_ERROR) {
442             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, 
443                          "getsockname failed");
444             continue;
445         }
446         len = sizeof(struct sockaddr);
447         context->sa_client = apr_palloc(context->ptrans, len);
448         if ((getpeername(context->accept_socket,
449                          context->sa_client, &len)) == SOCKET_ERROR) {
450             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, 
451                          "getpeername failed");
452             memset(&context->sa_client, '\0', sizeof(context->sa_client));
453         }
454         sockinfo.os_sock = &context->accept_socket;
455         sockinfo.local   = context->sa_server;
456         sockinfo.remote  = context->sa_client;
457         sockinfo.family  = APR_INET;
458         sockinfo.type    = SOCK_STREAM;
459         apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
460
461         return context;
462     }
463 }
464
465
466 /* Windows NT/2000 specific code...
467  * Accept processing for on Windows NT uses a producer/consumer queue 
468  * model. An accept thread accepts connections off the network then issues
469  * PostQueuedCompletionStatus() to awake a thread blocked on the ThreadDispatch 
470  * IOCompletionPort.
471  *
472  * winnt_accept()
473  *    One or more accept threads run in this function, each of which accepts 
474  *    connections off the network and calls PostQueuedCompletionStatus() to
475  *    queue an io completion packet to the ThreadDispatch IOCompletionPort.
476  * winnt_get_connection()
477  *    Worker threads block on the ThreadDispatch IOCompletionPort awaiting 
478  *    connections to service.
479  */
480 #define MAX_ACCEPTEX_ERR_COUNT 250
481 static unsigned int __stdcall winnt_accept(void *lr_) 
482 {
483     ap_listen_rec *lr = (ap_listen_rec *)lr_;
484     apr_os_sock_info_t sockinfo;
485     PCOMP_CONTEXT context = NULL;
486     DWORD BytesRead;
487     SOCKET nlsd;
488     int rv, err_count = 0;
489
490     apr_os_sock_get(&nlsd, lr->sd);
491
492     while (!shutdown_in_progress) {
493         if (!context) {
494             context = mpm_get_completion_context();
495             if (!context) {
496                 /* Temporary resource constraint? */
497                 Sleep(0);
498                 continue;
499             }
500         }
501
502         /* Create and initialize the accept socket */
503         if (context->accept_socket == INVALID_SOCKET) {
504             context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
505             if (context->accept_socket == INVALID_SOCKET) {
506                 /* Another temporary condition? */
507                 ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
508                              "winnt_accept: Failed to allocate an accept socket. "
509                              "Temporary resource constraint? Try again.");
510                 Sleep(100);
511                 continue;
512             }
513         }
514
515         /* AcceptEx on the completion context. The completion context will be 
516          * signaled when a connection is accepted. 
517          */
518         if (!AcceptEx(nlsd, context->accept_socket,
519                       context->buff,
520                       0,
521                       PADDED_ADDR_SIZE, 
522                       PADDED_ADDR_SIZE,
523                       &BytesRead,
524                       &context->Overlapped)) {
525             rv = apr_get_netos_error();
526             if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
527                 (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
528                 /* We can get here when:
529                  * 1) the client disconnects early
530                  * 2) TransmitFile does not properly recycle the accept socket (typically
531                  *    because the client disconnected)
532                  * 3) there is VPN or Firewall software installed with buggy AcceptEx implementation
533                  * 4) the webserver is using a dynamic address that has changed
534                  */
535                 ++err_count;
536                 closesocket(context->accept_socket);
537                 context->accept_socket = INVALID_SOCKET;
538                 if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
539                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
540                                  "Child %d: Encountered too many errors accepting client connections. "
541                                  "Possible causes: dynamic address renewal, or incompatible VPN or firewall software. "
542                                  "Try using the Win32DisableAcceptEx directive.", my_pid);
543                     err_count = 0;
544                 }
545                 continue;
546             }
547             else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
548                      (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
549                 ++err_count;
550                 closesocket(context->accept_socket);
551                 context->accept_socket = INVALID_SOCKET;
552                 if (err_count > MAX_ACCEPTEX_ERR_COUNT) { 
553                     ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
554                                  "Child %d: Encountered too many errors accepting client connections. "
555                                  "Possible causes: Unknown. "
556                                  "Try using the Win32DisableAcceptEx directive.", my_pid);
557                     err_count = 0;
558                 }
559                 continue;
560             }
561
562             /* Wait for pending i/o. 
563              * Wake up once per second to check for shutdown .
564              * XXX: We should be waiting on exit_event instead of polling
565              */
566             while (1) {
567                 rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
568                 if (rv == WAIT_OBJECT_0) {
569                     if (context->accept_socket == INVALID_SOCKET) {
570                         /* socket already closed */
571                         break;
572                     }
573                     if (!GetOverlappedResult((HANDLE)context->accept_socket, 
574                                              &context->Overlapped, 
575                                              &BytesRead, FALSE)) {
576                         ap_log_error(APLOG_MARK, APLOG_WARNING, 
577                                      apr_get_os_error(), ap_server_conf,
578                              "winnt_accept: Asynchronous AcceptEx failed.");
579                         closesocket(context->accept_socket);
580                         context->accept_socket = INVALID_SOCKET;
581                     }
582                     break;
583                 }
584                 /* WAIT_TIMEOUT */
585                 if (shutdown_in_progress) {
586                     closesocket(context->accept_socket);
587                     context->accept_socket = INVALID_SOCKET;
588                     break;
589                 }
590             }
591             if (context->accept_socket == INVALID_SOCKET) {
592                 continue;
593             }
594         }
595
596         err_count = 0;
597         /* Inherit the listen socket settings. Required for 
598          * shutdown() to work 
599          */
600         if (setsockopt(context->accept_socket, SOL_SOCKET,
601                        SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
602                        sizeof(nlsd))) {
603             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
604                          "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
605             /* Not a failure condition. Keep running. */
606         }
607
608         /* Get the local & remote address */
609         GetAcceptExSockaddrs(context->buff,
610                              0,
611                              PADDED_ADDR_SIZE,
612                              PADDED_ADDR_SIZE,
613                              &context->sa_server,
614                              &context->sa_server_len,
615                              &context->sa_client,
616                              &context->sa_client_len);
617
618         sockinfo.os_sock = &context->accept_socket;
619         sockinfo.local   = context->sa_server;
620         sockinfo.remote  = context->sa_client;
621         sockinfo.family  = APR_INET;
622         sockinfo.type    = SOCK_STREAM;
623         apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
624
625         /* When a connection is received, send an io completion notification to
626          * the ThreadDispatchIOCP. This function could be replaced by
627          * mpm_post_completion_context(), but why do an extra function call...
628          */
629         PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
630                                    &context->Overlapped);
631         context = NULL;
632     }
633     if (!shutdown_in_progress) {
634         /* Yow, hit an irrecoverable error! Tell the child to die. */
635         SetEvent(exit_event);
636     }
637     ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
638                  "Child %d: Accept thread exiting.", my_pid);
639     return 0;
640 }
641
642
643 static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
644 {
645     int rc;
646     DWORD BytesRead;
647     DWORD CompKey;
648     LPOVERLAPPED pol;
649
650     mpm_recycle_completion_context(context);
651
652     apr_atomic_inc(&g_blocked_threads);
653     while (1) {
654         if (workers_may_exit) {
655             apr_atomic_dec(&g_blocked_threads);
656             return NULL;
657         }
658         rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
659                                        &pol, INFINITE);
660         if (!rc) {
661             rc = apr_get_os_error();
662             ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
663                              "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc);
664             continue;
665         }
666
667         switch (CompKey) {
668         case IOCP_CONNECTION_ACCEPTED:
669             context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
670             break;
671         case IOCP_SHUTDOWN:
672             apr_atomic_dec(&g_blocked_threads);
673             return NULL;
674         default:
675             apr_atomic_dec(&g_blocked_threads);
676             return NULL;
677         }
678         break;
679     }
680     apr_atomic_dec(&g_blocked_threads);
681
682     return context;
683 }
684
685
686 /*
687  * worker_main()
688  * Main entry point for the worker threads. Worker threads block in 
689  * win*_get_connection() awaiting a connection to service.
690  */
691 static unsigned int __stdcall worker_main(void *thread_num_val)
692 {
693     static int requests_this_child = 0;
694     PCOMP_CONTEXT context = NULL;
695     int thread_num = (int)thread_num_val;
696     ap_sb_handle_t *sbh;
697
698     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
699                  "Child %d: Worker thread %ld starting.", my_pid, thread_num);
700     while (1) {
701         conn_rec *c;
702         apr_int32_t disconnected;
703
704         ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
705
706         /* Grab a connection off the network */
707         if (use_acceptex) {
708             context = winnt_get_connection(context);
709         }
710         else {
711             context = win9x_get_connection(context);
712         }
713         if (!context) {
714             /* Time for the thread to exit */
715             break;
716         }
717
718         /* Have we hit MaxRequestPerChild connections? */
719         if (ap_max_requests_per_child) {
720             requests_this_child++;
721             if (requests_this_child > ap_max_requests_per_child) {
722                 SetEvent(max_requests_per_child_event);
723             }
724         }
725
726         ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
727         c = ap_run_create_connection(context->ptrans, ap_server_conf,
728                                      context->sock, thread_num, sbh,
729                                      context->ba);
730
731         if (c) {
732             ap_process_connection(c, context->sock);
733             apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED, 
734                                &disconnected);
735             if (!disconnected) {
736                 context->accept_socket = INVALID_SOCKET;
737                 ap_lingering_close(c);
738             }
739             else if (!use_acceptex) {
740                 /* If the socket is disconnected but we are not using acceptex, 
741                  * we cannot reuse the socket. Disconnected sockets are removed
742                  * from the apr_socket_t struct by apr_sendfile() to prevent the
743                  * socket descriptor from being inadvertently closed by a call 
744                  * to apr_socket_close(), so close it directly.
745                  */
746                 closesocket(context->accept_socket);
747                 context->accept_socket = INVALID_SOCKET;
748             }
749         }
750         else {
751             /* ap_run_create_connection closes the socket on failure */
752             context->accept_socket = INVALID_SOCKET;
753         }
754     }
755
756     ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, 
757                                         (request_rec *) NULL);
758
759     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
760                  "Child %d: Worker thread %ld exiting.", my_pid, thread_num);
761     return 0;
762 }
763
764
765 static void cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean)
766 {
767     int i;
768
769     CloseHandle(handles[thread_to_clean]);
770     for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
771         handles[i] = handles[i + 1];
772     (*thread_cnt)--;
773 }
774
775
776 /*
777  * child_main() 
778  * Entry point for the main control thread for the child process. 
779  * This thread creates the accept thread, worker threads and
780  * monitors the child process for maintenance and shutdown
781  * events.
782  */
783 static void create_listener_thread()
784 {
785     int tid;
786     int num_listeners = 0;
787     if (!use_acceptex) {
788         _beginthreadex(NULL, 0, win9x_accept,
789                        NULL, 0, &tid);
790     } else {
791         /* Start an accept thread per listener 
792          * XXX: Why would we have a NULL sd in our listeners?
793          */
794         ap_listen_rec *lr;
795
796         /* Number of completion_contexts allowed in the system is
797          * (ap_threads_per_child + num_listeners). We need the additional
798          * completion contexts to prevent server hangs when ThreadsPerChild
799          * is configured to something less than or equal to the number
800          * of listeners. This is not a usual case, but people have 
801          * encountered it.
802          * */
803         for (lr = ap_listeners; lr ; lr = lr->next) {
804             num_listeners++;
805         }
806         max_num_completion_contexts = ap_threads_per_child + num_listeners;
807
808         /* Now start a thread per listener */
809         for (lr = ap_listeners; lr; lr = lr->next) {
810             if (lr->sd != NULL) {
811                 _beginthreadex(NULL, 1000, winnt_accept,
812                                (void *) lr, 0, &tid);
813             }
814         }
815     }
816 }
817
818
819 void child_main(apr_pool_t *pconf)
820 {
821     apr_status_t status;
822     apr_hash_t *ht;
823     ap_listen_rec *lr;
824     HANDLE child_events[2];
825     HANDLE *child_handles;
826     int listener_started = 0;
827     int threads_created = 0;
828     int watch_thread;
829     int time_remains;
830     int cld;
831     int tid;
832     int rv;
833     int i;
834
835     apr_pool_create(&pchild, pconf);
836     apr_pool_tag(pchild, "pchild");
837
838     ap_run_child_init(pchild, ap_server_conf);
839     ht = apr_hash_make(pchild);
840
841     /* Initialize the child_events */
842     max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL);
843     if (!max_requests_per_child_event) {
844         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
845                      "Child %d: Failed to create a max_requests event.", my_pid);
846         exit(APEXIT_CHILDINIT);
847     }
848     child_events[0] = exit_event;
849     child_events[1] = max_requests_per_child_event;
850
851     allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
852     apr_thread_mutex_create(&allowed_globals.jobmutex, 
853                             APR_THREAD_MUTEX_DEFAULT, pchild);
854
855     /*
856      * Wait until we have permission to start accepting connections.
857      * start_mutex is used to ensure that only one child ever
858      * goes into the listen/accept loop at once.
859      */
860     status = apr_proc_mutex_lock(start_mutex);
861     if (status != APR_SUCCESS) {
862         ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
863                      "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
864         exit(APEXIT_CHILDINIT);
865     }
866     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
867                  "Child %d: Acquired the start mutex.", my_pid);
868
869     /*
870      * Create the worker thread dispatch IOCompletionPort
871      * on Windows NT/2000
872      */
873     if (use_acceptex) {
874         /* Create the worker thread dispatch IOCP */
875         ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
876                                                     NULL,
877                                                     0,
878                                                     0); /* CONCURRENT ACTIVE THREADS */
879         apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
880         qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
881         if (!qwait_event) {
882             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
883                          "Child %d: Failed to create a qwait event.", my_pid);
884             exit(APEXIT_CHILDINIT);
885         }
886     }
887
888     /* 
889      * Create the pool of worker threads
890      */
891     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
892                  "Child %d: Starting %d worker threads.", my_pid, ap_threads_per_child);
893     child_handles = (HANDLE) apr_pcalloc(pchild, ap_threads_per_child * sizeof(int));
894     apr_thread_mutex_create(&child_lock, APR_THREAD_MUTEX_DEFAULT, pchild);
895
896     while (1) {
897         for (i = 0; i < ap_threads_per_child; i++) {
898             int *score_idx;
899             int status = ap_scoreboard_image->servers[0][i].status;
900             if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
901                 continue;
902             }
903             ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
904             child_handles[i] = (HANDLE) _beginthreadex(NULL, 0, worker_main,
905                                                        (void *) i, 0, &tid);
906             if (child_handles[i] == 0) {
907                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
908                              "Child %d: _beginthreadex failed. Unable to create all worker threads. "
909                              "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.", 
910                              my_pid, threads_created, ap_threads_per_child);
911                 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
912                 goto shutdown;
913             }
914             threads_created++;
915             /* Save the score board index in ht keyed to the thread handle. We need this 
916              * when cleaning up threads down below...
917              */
918             apr_thread_mutex_lock(child_lock);
919             score_idx = apr_pcalloc(pchild, sizeof(int));
920             *score_idx = i;
921             apr_hash_set(ht, &child_handles[i], sizeof(HANDLE), score_idx);
922             apr_thread_mutex_unlock(child_lock);
923         }
924         /* Start the listener only when workers are available */
925         if (!listener_started && threads_created) {
926             create_listener_thread();
927             listener_started = 1;
928             winnt_mpm_state = AP_MPMQ_RUNNING;
929         }
930         if (threads_created == ap_threads_per_child) {
931             break;
932         }
933         /* Check to see if the child has been told to exit */
934         if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
935             break;
936         }
937         /* wait for previous generation to clean up an entry in the scoreboard */
938         apr_sleep(1 * APR_USEC_PER_SEC);
939     }
940
941     /* Wait for one of three events:
942      * exit_event: 
943      *    The exit_event is signaled by the parent process to notify 
944      *    the child that it is time to exit.
945      *
946      * max_requests_per_child_event: 
947      *    This event is signaled by the worker threads to indicate that
948      *    the process has handled MaxRequestsPerChild connections.
949      *
950      * TIMEOUT:
951      *    To do periodic maintenance on the server (check for thread exits,
952      *    number of completion contexts, etc.)
953      *
954      * XXX: thread exits *aren't* being checked.
955      *
956      * XXX: other_child - we need the process handles to the other children
957      *      in order to map them to apr_proc_other_child_read (which is not
958      *      named well, it's more like a_p_o_c_died.)
959      *
960      * XXX: however - if we get a_p_o_c handle inheritance working, and
961      *      the parent process creates other children and passes the pipes 
962      *      to our worker processes, then we have no business doing such 
963      *      things in the child_main loop, but should happen in master_main.
964      */
965     while (1) {
966 #if !APR_HAS_OTHER_CHILD
967         rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE);
968         cld = rv - WAIT_OBJECT_0;
969 #else
970         rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
971         cld = rv - WAIT_OBJECT_0;
972         if (rv == WAIT_TIMEOUT) {
973             apr_proc_other_child_check();
974         }
975         else 
976 #endif
977             if (rv == WAIT_FAILED) {
978             /* Something serious is wrong */
979             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
980                          "Child %d: WAIT_FAILED -- shutting down server", my_pid);
981             break;
982         }
983         else if (cld == 0) {
984             /* Exit event was signaled */
985             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
986                          "Child %d: Exit event signaled. Child process is ending.", my_pid);
987             break;
988         }
989         else {
990             /* MaxRequestsPerChild event set by the worker threads.
991              * Signal the parent to restart
992              */
993             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
994                          "Child %d: Process exiting because it reached "
995                          "MaxRequestsPerChild. Signaling the parent to "
996                          "restart a new child process.", my_pid);
997             ap_signal_parent(SIGNAL_PARENT_RESTART);
998             break;
999         }
1000     }
1001
1002     /* 
1003      * Time to shutdown the child process 
1004      */
1005
1006  shutdown:
1007
1008     winnt_mpm_state = AP_MPMQ_STOPPING;
1009     /* Setting is_graceful will cause threads handling keep-alive connections 
1010      * to close the connection after handling the current request.
1011      */
1012     is_graceful = 1;
1013
1014     /* Close the listening sockets. Note, we must close the listeners
1015      * before closing any accept sockets pending in AcceptEx to prevent
1016      * memory leaks in the kernel.
1017      */
1018     for (lr = ap_listeners; lr ; lr = lr->next) {
1019         apr_socket_close(lr->sd);
1020     }
1021
1022     /* Shutdown listener threads and pending AcceptEx socksts 
1023      * but allow the worker threads to continue consuming from
1024      * the queue of accepted connections.
1025      */
1026     shutdown_in_progress = 1;
1027
1028     Sleep(1000);
1029
1030     /* Tell the worker threads to exit */
1031     workers_may_exit = 1;
1032
1033     /* Release the start_mutex to let the new process (in the restart
1034      * scenario) a chance to begin accepting and servicing requests 
1035      */
1036     rv = apr_proc_mutex_unlock(start_mutex);
1037     if (rv == APR_SUCCESS) {
1038         ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf, 
1039                      "Child %d: Released the start mutex", my_pid);
1040     }
1041     else {
1042         ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, 
1043                      "Child %d: Failure releasing the start mutex", my_pid);
1044     }
1045
1046     /* Shutdown the worker threads */
1047     if (!use_acceptex) {
1048         for (i = 0; i < threads_created; i++) {
1049             add_job(INVALID_SOCKET);
1050         }
1051     }
1052     else { /* Windows NT/2000 */
1053         /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
1054         while (g_blocked_threads > 0) {
1055             ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf, 
1056                          "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
1057             for (i=g_blocked_threads; i > 0; i--) {
1058                 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
1059             }
1060             Sleep(1000);
1061         }
1062         /* Empty the accept queue of completion contexts */
1063         apr_thread_mutex_lock(qlock);
1064         while (qhead) {
1065             CloseHandle(qhead->Overlapped.hEvent);
1066             closesocket(qhead->accept_socket);
1067             qhead = qhead->next;
1068         }
1069         apr_thread_mutex_unlock(qlock);
1070     }
1071
1072     /* Give busy threads a chance to service their connections,
1073      * (no more than the global server timeout period which 
1074      * we track in msec remaining).
1075      */
1076     watch_thread = 0;
1077     time_remains = (int)(ap_server_conf->timeout / APR_TIME_C(1000));
1078
1079     while (threads_created)
1080     {
1081         int nFailsafe = MAXIMUM_WAIT_OBJECTS;
1082         DWORD dwRet;
1083
1084         /* Every time we roll over to wait on the first group
1085          * of MAXIMUM_WAIT_OBJECTS threads, take a breather,
1086          * and infrequently update the error log.
1087          */
1088         if (watch_thread >= threads_created) {
1089             if ((time_remains -= 100) < 0)
1090                 break;
1091
1092             /* Every 30 seconds give an update */
1093             if ((time_remains % 30000) == 0) {
1094                 ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, 
1095                              ap_server_conf,
1096                              "Child %d: Waiting %d more seconds "
1097                              "for %d worker threads to finish.", 
1098                              my_pid, time_remains / 1000, threads_created);
1099             }
1100             /* We'll poll from the top, 10 times per second */
1101             Sleep(100);
1102             watch_thread = 0;
1103         }
1104
1105         /* Fairness, on each iteration we will pick up with the thread
1106          * after the one we just removed, even if it's a single thread.
1107          * We don't block here.
1108          */
1109         dwRet = WaitForMultipleObjects(min(threads_created - watch_thread,
1110                                            MAXIMUM_WAIT_OBJECTS),
1111                                        child_handles + watch_thread, 0, 0);
1112
1113         if (dwRet == WAIT_FAILED) {
1114             break;
1115         }
1116         if (dwRet == WAIT_TIMEOUT) {
1117             /* none ready */
1118             watch_thread += MAXIMUM_WAIT_OBJECTS;
1119             continue;
1120         }
1121         else if (dwRet >= WAIT_ABANDONED_0) {
1122             /* We just got the ownership of the object, which
1123              * should happen at most MAXIMUM_WAIT_OBJECTS times.
1124              * It does NOT mean that the object is signaled.
1125              */
1126             if ((nFailsafe--) < 1)
1127                 break;
1128         }
1129         else {
1130             watch_thread += (dwRet - WAIT_OBJECT_0);
1131             if (watch_thread >= threads_created)
1132                 break;
1133             cleanup_thread(child_handles, &threads_created, watch_thread);
1134         }
1135     }
1136
1137     /* Kill remaining threads off the hard way */
1138     if (threads_created) {
1139         ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1140                      "Child %d: Terminating %d threads that failed to exit.", 
1141                      my_pid, threads_created);
1142     }
1143     for (i = 0; i < threads_created; i++) {
1144         int *score_idx;
1145         TerminateThread(child_handles[i], 1);
1146         CloseHandle(child_handles[i]);
1147         /* Reset the scoreboard entry for the thread we just whacked */
1148         score_idx = apr_hash_get(ht, &child_handles[i], sizeof(HANDLE));
1149         ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL);        
1150     }
1151     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1152                  "Child %d: All worker threads have exited.", my_pid);
1153
1154     CloseHandle(allowed_globals.jobsemaphore);
1155     apr_thread_mutex_destroy(allowed_globals.jobmutex);
1156     apr_thread_mutex_destroy(child_lock);
1157
1158     if (use_acceptex) {
1159         apr_thread_mutex_destroy(qlock);
1160         CloseHandle(qwait_event);
1161     }
1162
1163     apr_pool_destroy(pchild);
1164     CloseHandle(exit_event);
1165 }
1166
1167 #endif /* def WIN32 */