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
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 AP_DECLARE(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);
83 context->ba = apr_bucket_alloc_create(context->ptrans);
85 ResetEvent(context->Overlapped.hEvent);
86 apr_thread_mutex_lock(qlock);
88 qtail->next = context;
91 SetEvent(qwait_event);
94 apr_thread_mutex_unlock(qlock);
98 AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void)
101 PCOMP_CONTEXT context = NULL;
104 /* Grab a context off the queue */
105 apr_thread_mutex_lock(qlock);
112 ResetEvent(qwait_event);
114 apr_thread_mutex_unlock(qlock);
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
122 if (num_completion_contexts >= max_num_completion_contexts) {
123 /* All workers are busy, need to wait for one */
124 static int reported = 0;
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");
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.
137 rv = WaitForSingleObject(qwait_event, 1000);
138 if (rv == WAIT_OBJECT_0)
140 else /* Hopefully, WAIT_TIMEOUT */
143 /* Allocate another context.
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...
148 apr_allocator_t *allocator;
150 apr_thread_mutex_lock(child_lock);
151 context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
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.");
159 apr_thread_mutex_unlock(child_lock);
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);
172 apr_thread_mutex_unlock(child_lock);
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);
181 apr_thread_mutex_unlock(child_lock);
185 /* Got a context from the queue */
193 AP_DECLARE(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(int 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 int 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 struct sockaddr_in sa_client;
333 int count_select_errors = 0;
337 struct fd_set listenfds;
338 SOCKET listenmaxfd = INVALID_SOCKET;
340 /* Setup the listeners
341 * ToDo: Use apr_poll()
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) {
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 rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
365 if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
366 count_select_errors = 0; /* reset count of errors */
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.
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.");
386 lr = find_ready_listener(&main_fds);
388 /* fetch the native socket descriptor */
389 apr_os_sock_get(&nsd, lr->sd);
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()));
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)");
408 SetEvent(exit_event);
413 static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
415 apr_os_sock_info_t sockinfo;
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);
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) {
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");
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));
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);
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
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.
480 #define MAX_ACCEPTEX_ERR_COUNT 250
481 static unsigned int __stdcall winnt_accept(void *lr_)
483 ap_listen_rec *lr = (ap_listen_rec *)lr_;
484 apr_os_sock_info_t sockinfo;
485 PCOMP_CONTEXT context = NULL;
488 int rv, err_count = 0;
490 apr_os_sock_get(&nlsd, lr->sd);
492 while (!shutdown_in_progress) {
494 context = mpm_get_completion_context();
496 /* Temporary resource constraint? */
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.");
515 /* AcceptEx on the completion context. The completion context will be
516 * signaled when a connection is accepted.
518 if (!AcceptEx(nlsd, context->accept_socket,
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
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);
547 else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
548 (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
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);
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
567 rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
568 if (rv == WAIT_OBJECT_0) {
569 if (context->accept_socket == INVALID_SOCKET) {
570 /* socket already closed */
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;
585 if (shutdown_in_progress) {
586 closesocket(context->accept_socket);
587 context->accept_socket = INVALID_SOCKET;
591 if (context->accept_socket == INVALID_SOCKET) {
597 /* Inherit the listen socket settings. Required for
600 if (setsockopt(context->accept_socket, SOL_SOCKET,
601 SO_UPDATE_ACCEPT_CONTEXT, (char *)&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. */
608 /* Get the local & remote address */
609 GetAcceptExSockaddrs(context->buff,
614 &context->sa_server_len,
616 &context->sa_client_len);
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);
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...
629 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
630 &context->Overlapped);
633 if (!shutdown_in_progress) {
634 /* Yow, hit an irrecoverable error! Tell the child to die. */
635 SetEvent(exit_event);
637 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
638 "Child %d: Accept thread exiting.", my_pid);
643 static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
650 mpm_recycle_completion_context(context);
652 apr_atomic_inc(&g_blocked_threads);
654 if (workers_may_exit) {
655 apr_atomic_dec(&g_blocked_threads);
658 rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
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);
668 case IOCP_CONNECTION_ACCEPTED:
669 context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
672 apr_atomic_dec(&g_blocked_threads);
675 apr_atomic_dec(&g_blocked_threads);
680 apr_atomic_dec(&g_blocked_threads);
688 * Main entry point for the worker threads. Worker threads block in
689 * win*_get_connection() awaiting a connection to service.
691 static unsigned int __stdcall worker_main(void *thread_num_val)
693 static int requests_this_child = 0;
694 PCOMP_CONTEXT context = NULL;
695 int thread_num = (int)thread_num_val;
698 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
699 "Child %d: Worker thread %ld starting.", my_pid, thread_num);
702 apr_int32_t disconnected;
704 ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
706 /* Grab a connection off the network */
708 context = winnt_get_connection(context);
711 context = win9x_get_connection(context);
714 /* Time for the thread to exit */
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);
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,
732 ap_process_connection(c, context->sock);
733 apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
736 context->accept_socket = INVALID_SOCKET;
737 ap_lingering_close(c);
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.
746 closesocket(context->accept_socket);
747 context->accept_socket = INVALID_SOCKET;
751 /* ap_run_create_connection closes the socket on failure */
752 context->accept_socket = INVALID_SOCKET;
756 ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD,
757 (request_rec *) NULL);
759 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
760 "Child %d: Worker thread %ld exiting.", my_pid, thread_num);
765 static void cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean)
769 CloseHandle(handles[thread_to_clean]);
770 for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
771 handles[i] = handles[i + 1];
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
783 static void create_listener_thread()
786 int num_listeners = 0;
788 _beginthreadex(NULL, 0, win9x_accept,
791 /* Start an accept thread per listener
792 * XXX: Why would we have a NULL sd in our listeners?
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
803 for (lr = ap_listeners; lr ; lr = lr->next) {
806 max_num_completion_contexts = ap_threads_per_child + num_listeners;
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);
819 void child_main(apr_pool_t *pconf)
824 HANDLE child_events[2];
825 HANDLE *child_handles;
826 int listener_started = 0;
827 int threads_created = 0;
835 apr_pool_create(&pchild, pconf);
836 apr_pool_tag(pchild, "pchild");
838 ap_run_child_init(pchild, ap_server_conf);
839 ht = apr_hash_make(pchild);
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);
848 child_events[0] = exit_event;
849 child_events[1] = max_requests_per_child_event;
851 allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
852 apr_thread_mutex_create(&allowed_globals.jobmutex,
853 APR_THREAD_MUTEX_DEFAULT, pchild);
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.
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);
866 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
867 "Child %d: Acquired the start mutex.", my_pid);
870 * Create the worker thread dispatch IOCompletionPort
874 /* Create the worker thread dispatch IOCP */
875 ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
878 0); /* CONCURRENT ACTIVE THREADS */
879 apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
880 qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
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);
889 * Create the pool of worker threads
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);
897 for (i = 0; i < ap_threads_per_child; i++) {
899 int status = ap_scoreboard_image->servers[0][i].status;
900 if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
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);
915 /* Save the score board index in ht keyed to the thread handle. We need this
916 * when cleaning up threads down below...
918 apr_thread_mutex_lock(child_lock);
919 score_idx = apr_pcalloc(pchild, sizeof(int));
921 apr_hash_set(ht, &child_handles[i], sizeof(HANDLE), score_idx);
922 apr_thread_mutex_unlock(child_lock);
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;
930 if (threads_created == ap_threads_per_child) {
933 /* Check to see if the child has been told to exit */
934 if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
937 /* wait for previous generation to clean up an entry in the scoreboard */
938 apr_sleep(1 * APR_USEC_PER_SEC);
941 /* Wait for one of three events:
943 * The exit_event is signaled by the parent process to notify
944 * the child that it is time to exit.
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.
951 * To do periodic maintenance on the server (check for thread exits,
952 * number of completion contexts, etc.)
954 * XXX: thread exits *aren't* being checked.
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.)
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.
966 #if !APR_HAS_OTHER_CHILD
967 rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE);
968 cld = rv - WAIT_OBJECT_0;
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();
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);
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);
990 /* MaxRequestsPerChild event set by the worker threads.
991 * Signal the parent to restart
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);
1003 * Time to shutdown the child process
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.
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.
1018 for (lr = ap_listeners; lr ; lr = lr->next) {
1019 apr_socket_close(lr->sd);
1022 /* Shutdown listener threads and pending AcceptEx socksts
1023 * but allow the worker threads to continue consuming from
1024 * the queue of accepted connections.
1026 shutdown_in_progress = 1;
1030 /* Tell the worker threads to exit */
1031 workers_may_exit = 1;
1033 /* Release the start_mutex to let the new process (in the restart
1034 * scenario) a chance to begin accepting and servicing requests
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);
1042 ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1043 "Child %d: Failure releasing the start mutex", my_pid);
1046 /* Shutdown the worker threads */
1047 if (!use_acceptex) {
1048 for (i = 0; i < threads_created; i++) {
1049 add_job(INVALID_SOCKET);
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);
1062 /* Empty the accept queue of completion contexts */
1063 apr_thread_mutex_lock(qlock);
1065 CloseHandle(qhead->Overlapped.hEvent);
1066 closesocket(qhead->accept_socket);
1067 qhead = qhead->next;
1069 apr_thread_mutex_unlock(qlock);
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).
1077 time_remains = (int)(ap_server_conf->timeout / APR_TIME_C(1000));
1079 while (threads_created)
1081 int nFailsafe = MAXIMUM_WAIT_OBJECTS;
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.
1088 if (watch_thread >= threads_created) {
1089 if ((time_remains -= 100) < 0)
1092 /* Every 30 seconds give an update */
1093 if ((time_remains % 30000) == 0) {
1094 ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS,
1096 "Child %d: Waiting %d more seconds "
1097 "for %d worker threads to finish.",
1098 my_pid, time_remains / 1000, threads_created);
1100 /* We'll poll from the top, 10 times per second */
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.
1109 dwRet = WaitForMultipleObjects(min(threads_created - watch_thread,
1110 MAXIMUM_WAIT_OBJECTS),
1111 child_handles + watch_thread, 0, 0);
1113 if (dwRet == WAIT_FAILED) {
1116 if (dwRet == WAIT_TIMEOUT) {
1118 watch_thread += MAXIMUM_WAIT_OBJECTS;
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.
1126 if ((nFailsafe--) < 1)
1130 watch_thread += (dwRet - WAIT_OBJECT_0);
1131 if (watch_thread >= threads_created)
1133 cleanup_thread(child_handles, &threads_created, watch_thread);
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);
1143 for (i = 0; i < threads_created; i++) {
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);
1151 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1152 "Child %d: All worker threads have exited.", my_pid);
1154 CloseHandle(allowed_globals.jobsemaphore);
1155 apr_thread_mutex_destroy(allowed_globals.jobmutex);
1156 apr_thread_mutex_destroy(child_lock);
1159 apr_thread_mutex_destroy(qlock);
1160 CloseHandle(qwait_event);
1163 apr_pool_destroy(pchild);
1164 CloseHandle(exit_event);
1167 #endif /* def WIN32 */