1 /* Copyright 2002-2006 The Apache Software Foundation or its licensors, as
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "http_main.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"
32 #include "apr_thread_mutex.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"
40 #include "apr_atomic.h"
42 /* shared with mpm_winnt.c */
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;
50 /* child_main() should never need to modify is_graceful!?! */
51 extern int volatile is_graceful;
53 /* Queue for managing the passing of COMP_CONTEXTs between
54 * the accept and worker threads.
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;
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;
72 void mpm_recycle_completion_context(PCOMP_CONTEXT context)
74 /* Recycle the completion context.
75 * - clear the ptrans pool
76 * - put the context on the queue to be consumed by the accept thread
78 * context->accept_socket may be in a disconnected but reusable
79 * state so -don't- close it.
82 apr_pool_clear(context->ptrans);
84 ResetEvent(context->Overlapped.hEvent);
85 apr_thread_mutex_lock(qlock);
87 qtail->next = context;
90 SetEvent(qwait_event);
93 apr_thread_mutex_unlock(qlock);
97 PCOMP_CONTEXT mpm_get_completion_context(void)
100 PCOMP_CONTEXT context = NULL;
103 /* Grab a context off the queue */
104 apr_thread_mutex_lock(qlock);
111 ResetEvent(qwait_event);
113 apr_thread_mutex_unlock(qlock);
116 /* We failed to grab a context off the queue, consider allocating
117 * a new one out of the child pool. There may be up to
118 * (ap_threads_per_child + num_listeners) contexts in the system
121 if (num_completion_contexts >= max_num_completion_contexts) {
122 /* All workers are busy, need to wait for one */
123 static int reported = 0;
125 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
126 "Server ran out of threads to serve requests. Consider "
127 "raising the ThreadsPerChild setting");
131 /* Wait for a worker to free a context. Once per second, give
132 * the caller a chance to check for shutdown. If the wait
133 * succeeds, get the context off the queue. It must be available,
134 * since there's only one consumer.
136 rv = WaitForSingleObject(qwait_event, 1000);
137 if (rv == WAIT_OBJECT_0)
139 else /* Hopefully, WAIT_TIMEOUT */
142 /* Allocate another context.
144 * Multiple failures in the next two steps will cause the pchild pool
145 * to 'leak' storage. I don't think this is worth fixing...
147 apr_allocator_t *allocator;
149 apr_thread_mutex_lock(child_lock);
150 context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
152 context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
153 if (context->Overlapped.hEvent == NULL) {
154 /* Hopefully this is a temporary condition ... */
155 ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
156 "mpm_get_completion_context: CreateEvent failed.");
158 apr_thread_mutex_unlock(child_lock);
162 /* Create the tranaction pool */
163 apr_allocator_create(&allocator);
164 apr_allocator_max_free_set(allocator, ap_max_mem_free);
165 rv = apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
166 if (rv != APR_SUCCESS) {
167 ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
168 "mpm_get_completion_context: Failed to create the transaction pool.");
169 CloseHandle(context->Overlapped.hEvent);
171 apr_thread_mutex_unlock(child_lock);
174 apr_allocator_owner_set(allocator, context->ptrans);
175 apr_pool_tag(context->ptrans, "transaction");
177 context->accept_socket = INVALID_SOCKET;
178 context->ba = apr_bucket_alloc_create(pchild);
179 apr_atomic_inc32(&num_completion_contexts);
181 apr_thread_mutex_unlock(child_lock);
185 /* Got a context from the queue */
193 apr_status_t mpm_post_completion_context(PCOMP_CONTEXT context,
196 LPOVERLAPPED pOverlapped;
198 pOverlapped = &context->Overlapped;
202 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped);
208 * find_ready_listener()
209 * Only used by Win9* and should go away when the win9*_accept() function is
210 * reimplemented using apr_poll().
212 static ap_listen_rec *head_listener;
214 static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
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;
233 } while (lr != head_listener);
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.
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.
255 typedef struct joblist_s {
256 struct joblist_s *next;
260 typedef struct globals_s {
264 apr_thread_mutex_t *jobmutex;
268 globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
270 #define MAX_SELECT_ERRORS 100
273 static void add_job(SOCKET sock)
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()!");
283 new_job->next = NULL;
284 new_job->sock = sock;
286 apr_thread_mutex_lock(allowed_globals.jobmutex);
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);
296 apr_thread_mutex_unlock(allowed_globals.jobmutex);
300 static SOCKET remove_job(void)
305 WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
306 apr_thread_mutex_lock(allowed_globals.jobmutex);
308 if (shutdown_in_progress && !allowed_globals.jobhead) {
309 apr_thread_mutex_unlock(allowed_globals.jobmutex);
310 return (INVALID_SOCKET);
312 job = allowed_globals.jobhead;
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);
325 static unsigned int __stdcall win9x_accept(void * dummy)
331 SOCKET nsd = INVALID_SOCKET;
332 int count_select_errors = 0;
336 struct fd_set listenfds;
338 struct sockaddr_in6 sa_client;
340 struct sockaddr_in sa_client;
343 /* Setup the listeners
344 * ToDo: Use apr_poll()
347 for (lr = ap_listeners; lr; lr = lr->next) {
348 if (lr->sd != NULL) {
349 apr_os_sock_get(&nsd, lr->sd);
350 FD_SET(nsd, &listenfds);
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);
356 head_listener = ap_listeners;
358 while (!shutdown_in_progress) {
359 tv.tv_sec = wait_time;
361 memcpy(&main_fds, &listenfds, sizeof(fd_set));
363 /* First parameter of select() is ignored on Windows */
364 rc = select(0, &main_fds, NULL, NULL, &tv);
366 if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
367 count_select_errors = 0; /* reset count of errors */
370 else if (rc == SOCKET_ERROR) {
371 /* A "real" error occurred, log it and increment the count of
372 * select errors. This count is used to ensure we don't go into
373 * a busy loop of continuous errors.
375 ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf,
376 "select failed with error %d", apr_get_netos_error());
377 count_select_errors++;
378 if (count_select_errors > MAX_SELECT_ERRORS) {
379 shutdown_in_progress = 1;
380 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
381 "Too many errors in select loop. Child process exiting.");
387 lr = find_ready_listener(&main_fds);
389 /* fetch the native socket descriptor */
390 apr_os_sock_get(&nsd, lr->sd);
395 clen = sizeof(sa_client);
396 csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
397 } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
400 if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
401 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
402 "accept: (client socket)");
409 SetEvent(exit_event);
414 static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
416 apr_os_sock_info_t sockinfo;
419 salen = sizeof(struct sockaddr_in6);
421 salen = sizeof(struct sockaddr_in);
425 if (context == NULL) {
426 /* allocate the completion context and the transaction pool */
427 apr_allocator_t *allocator;
428 apr_thread_mutex_lock(child_lock);
429 context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
430 apr_allocator_create(&allocator);
431 apr_allocator_max_free_set(allocator, ap_max_mem_free);
432 apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
433 apr_allocator_owner_set(allocator, context->ptrans);
434 apr_pool_tag(context->ptrans, "transaction");
435 context->ba = apr_bucket_alloc_create(pchild);
436 apr_thread_mutex_unlock(child_lock);
440 apr_pool_clear(context->ptrans);
441 context->accept_socket = remove_job();
442 if (context->accept_socket == INVALID_SOCKET) {
446 context->sa_server = apr_palloc(context->ptrans, len);
447 if (getsockname(context->accept_socket,
448 context->sa_server, &len)== SOCKET_ERROR) {
449 ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
450 "getsockname failed");
454 context->sa_client = apr_palloc(context->ptrans, len);
455 if ((getpeername(context->accept_socket,
456 context->sa_client, &len)) == SOCKET_ERROR) {
457 ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
458 "getpeername failed");
459 memset(&context->sa_client, '\0', sizeof(context->sa_client));
461 sockinfo.os_sock = &context->accept_socket;
462 sockinfo.local = context->sa_server;
463 sockinfo.remote = context->sa_client;
464 sockinfo.family = context->sa_server->sa_family;
465 sockinfo.type = SOCK_STREAM;
466 apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
473 /* Windows NT/2000 specific code...
474 * Accept processing for on Windows NT uses a producer/consumer queue
475 * model. An accept thread accepts connections off the network then issues
476 * PostQueuedCompletionStatus() to awake a thread blocked on the ThreadDispatch
480 * One or more accept threads run in this function, each of which accepts
481 * connections off the network and calls PostQueuedCompletionStatus() to
482 * queue an io completion packet to the ThreadDispatch IOCompletionPort.
483 * winnt_get_connection()
484 * Worker threads block on the ThreadDispatch IOCompletionPort awaiting
485 * connections to service.
487 #define MAX_ACCEPTEX_ERR_COUNT 100
488 static unsigned int __stdcall winnt_accept(void *lr_)
490 ap_listen_rec *lr = (ap_listen_rec *)lr_;
491 apr_os_sock_info_t sockinfo;
492 PCOMP_CONTEXT context = NULL;
495 int rv, err_count = 0;
497 SOCKADDR_STORAGE ss_listen;
498 int namelen = sizeof(ss_listen);
501 apr_os_sock_get(&nlsd, lr->sd);
504 if (getsockname(nlsd, (struct sockaddr *)&ss_listen, &namelen) == SOCKET_ERROR) {
505 ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_netos_error(), ap_server_conf,
506 "winnt_accept: getsockname error on listening socket, is IPv6 available?");
511 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
512 "Child %d: Starting thread to listen on port %d.", my_pid, lr->bind_addr->port);
513 while (!shutdown_in_progress) {
515 context = mpm_get_completion_context();
517 /* Temporary resource constraint? */
523 /* Create and initialize the accept socket */
525 if (context->accept_socket == INVALID_SOCKET) {
526 context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
527 context->socket_family = ss_listen.ss_family;
529 else if (context->socket_family != ss_listen.ss_family) {
530 closesocket(context->accept_socket);
531 context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
532 context->socket_family = ss_listen.ss_family;
535 if (context->accept_socket == INVALID_SOCKET) {
536 ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
537 "winnt_accept: Failed to allocate an accept socket. "
538 "Temporary resource constraint? Try again.");
543 if (context->accept_socket == INVALID_SOCKET) {
544 context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
545 if (context->accept_socket == INVALID_SOCKET) {
546 /* Another temporary condition? */
547 ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
548 "winnt_accept: Failed to allocate an accept socket. "
549 "Temporary resource constraint? Try again.");
555 /* AcceptEx on the completion context. The completion context will be
556 * signaled when a connection is accepted.
558 if (!AcceptEx(nlsd, context->accept_socket,
564 &context->Overlapped)) {
565 rv = apr_get_netos_error();
566 if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
567 (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
568 /* We can get here when:
569 * 1) the client disconnects early
570 * 2) TransmitFile does not properly recycle the accept socket (typically
571 * because the client disconnected)
572 * 3) there is VPN or Firewall software installed with buggy AcceptEx implementation
573 * 4) the webserver is using a dynamic address that has changed
576 closesocket(context->accept_socket);
577 context->accept_socket = INVALID_SOCKET;
578 if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
579 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
580 "Child %d: Encountered too many errors accepting client connections. "
581 "Possible causes: dynamic address renewal, or incompatible VPN or firewall software. "
582 "Try using the Win32DisableAcceptEx directive.", my_pid);
587 else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
588 (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
590 if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
591 ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
592 "Child %d: Encountered too many errors accepting client connections. "
593 "Possible causes: Unknown. "
594 "Try using the Win32DisableAcceptEx directive.", my_pid);
597 closesocket(context->accept_socket);
598 context->accept_socket = INVALID_SOCKET;
603 /* Wait for pending i/o.
604 * Wake up once per second to check for shutdown .
605 * XXX: We should be waiting on exit_event instead of polling
608 rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
609 if (rv == WAIT_OBJECT_0) {
610 if (context->accept_socket == INVALID_SOCKET) {
611 /* socket already closed */
614 if (!GetOverlappedResult((HANDLE)context->accept_socket,
615 &context->Overlapped,
616 &BytesRead, FALSE)) {
617 ap_log_error(APLOG_MARK, APLOG_WARNING,
618 apr_get_os_error(), ap_server_conf,
619 "winnt_accept: Asynchronous AcceptEx failed.");
620 closesocket(context->accept_socket);
621 context->accept_socket = INVALID_SOCKET;
626 if (shutdown_in_progress) {
627 closesocket(context->accept_socket);
628 context->accept_socket = INVALID_SOCKET;
632 if (context->accept_socket == INVALID_SOCKET) {
637 /* Inherit the listen socket settings. Required for
640 if (setsockopt(context->accept_socket, SOL_SOCKET,
641 SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
643 ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
644 "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
645 /* Not a failure condition. Keep running. */
648 /* Get the local & remote address */
649 GetAcceptExSockaddrs(context->buff,
654 &context->sa_server_len,
656 &context->sa_client_len);
658 sockinfo.os_sock = &context->accept_socket;
659 sockinfo.local = context->sa_server;
660 sockinfo.remote = context->sa_client;
661 sockinfo.family = context->sa_server->sa_family;
662 sockinfo.type = SOCK_STREAM;
663 apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
665 /* When a connection is received, send an io completion notification to
666 * the ThreadDispatchIOCP. This function could be replaced by
667 * mpm_post_completion_context(), but why do an extra function call...
669 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
670 &context->Overlapped);
673 if (!shutdown_in_progress) {
674 /* Yow, hit an irrecoverable error! Tell the child to die. */
675 SetEvent(exit_event);
677 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
678 "Child %d: Accept thread exiting.", my_pid);
683 static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
694 mpm_recycle_completion_context(context);
696 apr_atomic_inc32(&g_blocked_threads);
698 if (workers_may_exit) {
699 apr_atomic_dec32(&g_blocked_threads);
702 rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
705 rc = apr_get_os_error();
706 ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
707 "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc);
712 case IOCP_CONNECTION_ACCEPTED:
713 context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
716 apr_atomic_dec32(&g_blocked_threads);
719 apr_atomic_dec32(&g_blocked_threads);
724 apr_atomic_dec32(&g_blocked_threads);
732 * Main entry point for the worker threads. Worker threads block in
733 * win*_get_connection() awaiting a connection to service.
735 static unsigned int __stdcall worker_main(void *thread_num_val)
737 static int requests_this_child = 0;
738 PCOMP_CONTEXT context = NULL;
739 int thread_num = (int)thread_num_val;
744 apr_int32_t disconnected;
746 ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
748 /* Grab a connection off the network */
750 context = winnt_get_connection(context);
753 context = win9x_get_connection(context);
757 /* Time for the thread to exit */
761 /* Have we hit MaxRequestPerChild connections? */
762 if (ap_max_requests_per_child) {
763 requests_this_child++;
764 if (requests_this_child > ap_max_requests_per_child) {
765 SetEvent(max_requests_per_child_event);
769 ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
770 c = ap_run_create_connection(context->ptrans, ap_server_conf,
771 context->sock, thread_num, sbh,
775 ap_process_connection(c, context->sock);
776 apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
779 context->accept_socket = INVALID_SOCKET;
780 ap_lingering_close(c);
782 else if (!use_acceptex) {
783 /* If the socket is disconnected but we are not using acceptex,
784 * we cannot reuse the socket. Disconnected sockets are removed
785 * from the apr_socket_t struct by apr_sendfile() to prevent the
786 * socket descriptor from being inadvertently closed by a call
787 * to apr_socket_close(), so close it directly.
789 closesocket(context->accept_socket);
790 context->accept_socket = INVALID_SOCKET;
794 /* ap_run_create_connection closes the socket on failure */
795 context->accept_socket = INVALID_SOCKET;
799 ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD,
800 (request_rec *) NULL);
806 static void cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean)
810 CloseHandle(handles[thread_to_clean]);
811 for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
812 handles[i] = handles[i + 1];
819 * Entry point for the main control thread for the child process.
820 * This thread creates the accept thread, worker threads and
821 * monitors the child process for maintenance and shutdown
824 static void create_listener_thread()
827 int num_listeners = 0;
829 _beginthreadex(NULL, 0, win9x_accept,
832 /* Start an accept thread per listener
833 * XXX: Why would we have a NULL sd in our listeners?
837 /* Number of completion_contexts allowed in the system is
838 * (ap_threads_per_child + num_listeners). We need the additional
839 * completion contexts to prevent server hangs when ThreadsPerChild
840 * is configured to something less than or equal to the number
841 * of listeners. This is not a usual case, but people have
844 for (lr = ap_listeners; lr ; lr = lr->next) {
847 max_num_completion_contexts = ap_threads_per_child + num_listeners;
849 /* Now start a thread per listener */
850 for (lr = ap_listeners; lr; lr = lr->next) {
851 if (lr->sd != NULL) {
852 _beginthreadex(NULL, 1000, winnt_accept,
853 (void *) lr, 0, &tid);
860 void child_main(apr_pool_t *pconf)
865 HANDLE child_events[2];
866 int threads_created = 0;
867 int listener_started = 0;
869 HANDLE *child_handles;
875 apr_pool_create(&pchild, pconf);
876 apr_pool_tag(pchild, "pchild");
878 ap_run_child_init(pchild, ap_server_conf);
879 ht = apr_hash_make(pchild);
881 /* Initialize the child_events */
882 max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL);
883 if (!max_requests_per_child_event) {
884 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
885 "Child %d: Failed to create a max_requests event.", my_pid);
886 exit(APEXIT_CHILDINIT);
888 child_events[0] = exit_event;
889 child_events[1] = max_requests_per_child_event;
891 allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
892 apr_thread_mutex_create(&allowed_globals.jobmutex,
893 APR_THREAD_MUTEX_DEFAULT, pchild);
896 * Wait until we have permission to start accepting connections.
897 * start_mutex is used to ensure that only one child ever
898 * goes into the listen/accept loop at once.
900 status = apr_proc_mutex_lock(start_mutex);
901 if (status != APR_SUCCESS) {
902 ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
903 "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
904 exit(APEXIT_CHILDINIT);
906 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
907 "Child %d: Acquired the start mutex.", my_pid);
910 * Create the worker thread dispatch IOCompletionPort
914 /* Create the worker thread dispatch IOCP */
915 ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
918 0); /* CONCURRENT ACTIVE THREADS */
919 apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
920 qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
922 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
923 "Child %d: Failed to create a qwait event.", my_pid);
924 exit(APEXIT_CHILDINIT);
929 * Create the pool of worker threads
931 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
932 "Child %d: Starting %d worker threads.", my_pid, ap_threads_per_child);
933 child_handles = (HANDLE) apr_pcalloc(pchild, ap_threads_per_child * sizeof(HANDLE));
934 apr_thread_mutex_create(&child_lock, APR_THREAD_MUTEX_DEFAULT, pchild);
937 for (i = 0; i < ap_threads_per_child; i++) {
939 int status = ap_scoreboard_image->servers[0][i].status;
940 if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
943 ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
944 child_handles[i] = (HANDLE) _beginthreadex(NULL, (unsigned)ap_thread_stacksize,
945 worker_main, (void *) i, 0, &tid);
946 if (child_handles[i] == 0) {
947 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
948 "Child %d: _beginthreadex failed. Unable to create all worker threads. "
949 "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.",
950 my_pid, threads_created, ap_threads_per_child);
951 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
955 /* Save the score board index in ht keyed to the thread handle. We need this
956 * when cleaning up threads down below...
958 apr_thread_mutex_lock(child_lock);
959 score_idx = apr_pcalloc(pchild, sizeof(int));
961 apr_hash_set(ht, &child_handles[i], sizeof(HANDLE), score_idx);
962 apr_thread_mutex_unlock(child_lock);
964 /* Start the listener only when workers are available */
965 if (!listener_started && threads_created) {
966 create_listener_thread();
967 listener_started = 1;
968 winnt_mpm_state = AP_MPMQ_RUNNING;
970 if (threads_created == ap_threads_per_child) {
973 /* Check to see if the child has been told to exit */
974 if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
977 /* wait for previous generation to clean up an entry in the scoreboard */
978 apr_sleep(1 * APR_USEC_PER_SEC);
981 /* Wait for one of three events:
983 * The exit_event is signaled by the parent process to notify
984 * the child that it is time to exit.
986 * max_requests_per_child_event:
987 * This event is signaled by the worker threads to indicate that
988 * the process has handled MaxRequestsPerChild connections.
991 * To do periodic maintenance on the server (check for thread exits,
992 * number of completion contexts, etc.)
994 * XXX: thread exits *aren't* being checked.
996 * XXX: other_child - we need the process handles to the other children
997 * in order to map them to apr_proc_other_child_read (which is not
998 * named well, it's more like a_p_o_c_died.)
1000 * XXX: however - if we get a_p_o_c handle inheritance working, and
1001 * the parent process creates other children and passes the pipes
1002 * to our worker processes, then we have no business doing such
1003 * things in the child_main loop, but should happen in master_main.
1006 #if !APR_HAS_OTHER_CHILD
1007 rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE);
1008 cld = rv - WAIT_OBJECT_0;
1010 rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
1011 cld = rv - WAIT_OBJECT_0;
1012 if (rv == WAIT_TIMEOUT) {
1013 apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
1017 if (rv == WAIT_FAILED) {
1018 /* Something serious is wrong */
1019 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1020 "Child %d: WAIT_FAILED -- shutting down server", my_pid);
1023 else if (cld == 0) {
1024 /* Exit event was signaled */
1025 ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1026 "Child %d: Exit event signaled. Child process is ending.", my_pid);
1030 /* MaxRequestsPerChild event set by the worker threads.
1031 * Signal the parent to restart
1033 ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1034 "Child %d: Process exiting because it reached "
1035 "MaxRequestsPerChild. Signaling the parent to "
1036 "restart a new child process.", my_pid);
1037 ap_signal_parent(SIGNAL_PARENT_RESTART);
1043 * Time to shutdown the child process
1048 winnt_mpm_state = AP_MPMQ_STOPPING;
1049 /* Setting is_graceful will cause threads handling keep-alive connections
1050 * to close the connection after handling the current request.
1054 /* Close the listening sockets. Note, we must close the listeners
1055 * before closing any accept sockets pending in AcceptEx to prevent
1056 * memory leaks in the kernel.
1058 for (lr = ap_listeners; lr ; lr = lr->next) {
1059 apr_socket_close(lr->sd);
1062 /* Shutdown listener threads and pending AcceptEx socksts
1063 * but allow the worker threads to continue consuming from
1064 * the queue of accepted connections.
1066 shutdown_in_progress = 1;
1070 /* Tell the worker threads to exit */
1071 workers_may_exit = 1;
1073 /* Release the start_mutex to let the new process (in the restart
1074 * scenario) a chance to begin accepting and servicing requests
1076 rv = apr_proc_mutex_unlock(start_mutex);
1077 if (rv == APR_SUCCESS) {
1078 ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf,
1079 "Child %d: Released the start mutex", my_pid);
1082 ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1083 "Child %d: Failure releasing the start mutex", my_pid);
1086 /* Shutdown the worker threads */
1087 if (!use_acceptex) {
1088 for (i = 0; i < threads_created; i++) {
1089 add_job(INVALID_SOCKET);
1092 else { /* Windows NT/2000 */
1093 /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
1094 while (g_blocked_threads > 0) {
1095 ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf,
1096 "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
1097 for (i=g_blocked_threads; i > 0; i--) {
1098 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
1102 /* Empty the accept queue of completion contexts */
1103 apr_thread_mutex_lock(qlock);
1105 CloseHandle(qhead->Overlapped.hEvent);
1106 closesocket(qhead->accept_socket);
1107 qhead = qhead->next;
1109 apr_thread_mutex_unlock(qlock);
1112 /* Give busy worker threads a chance to service their connections */
1113 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1114 "Child %d: Waiting for %d worker threads to exit.", my_pid, threads_created);
1115 end_time = time(NULL) + 180;
1116 while (threads_created) {
1117 rv = wait_for_many_objects(threads_created, child_handles, (DWORD)(end_time - time(NULL)));
1118 if (rv != WAIT_TIMEOUT) {
1119 rv = rv - WAIT_OBJECT_0;
1120 ap_assert((rv >= 0) && (rv < threads_created));
1121 cleanup_thread(child_handles, &threads_created, rv);
1127 /* Kill remaining threads off the hard way */
1128 if (threads_created) {
1129 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1130 "Child %d: Terminating %d threads that failed to exit.",
1131 my_pid, threads_created);
1133 for (i = 0; i < threads_created; i++) {
1135 TerminateThread(child_handles[i], 1);
1136 CloseHandle(child_handles[i]);
1137 /* Reset the scoreboard entry for the thread we just whacked */
1138 score_idx = apr_hash_get(ht, &child_handles[i], sizeof(HANDLE));
1139 ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL);
1141 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1142 "Child %d: All worker threads have exited.", my_pid);
1144 CloseHandle(allowed_globals.jobsemaphore);
1145 apr_thread_mutex_destroy(allowed_globals.jobmutex);
1146 apr_thread_mutex_destroy(child_lock);
1149 apr_thread_mutex_destroy(qlock);
1150 CloseHandle(qwait_event);
1153 apr_pool_destroy(pchild);
1154 CloseHandle(exit_event);
1157 #endif /* def WIN32 */