1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
62 #define INCL_DOSERRORS
64 #include "ap_config.h"
66 #include "mpm_default.h"
67 #include "http_main.h"
69 #include "http_config.h"
70 #include "http_core.h" /* for get_remote_host */
71 #include "http_connection.h"
74 #include "ap_listen.h"
75 #include "apr_portable.h"
77 #include "mpm_common.h"
78 #include "apr_strings.h"
82 /* XXXXXX move these to header file private to this MPM */
84 /* We don't need many processes,
85 * they're only for redundancy in the event of a crash
87 #define HARD_SERVER_LIMIT 10
89 /* Limit on the total number of threads per process
91 #ifndef HARD_THREAD_LIMIT
92 #define HARD_THREAD_LIMIT 256
95 #define ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t)
99 apr_socket_t *conn_sd;
102 #define WORKTYPE_CONN 0
103 #define WORKTYPE_EXIT 1
105 static apr_pool_t *pchild = NULL;
106 static int child_slot;
107 static int shutdown_pending = 0;
108 extern int ap_my_generation;
109 static int volatile is_graceful = 1;
110 HEV shutdown_event; /* signaled when this child is shutting down */
112 /* grab some MPM globals */
113 extern int ap_min_spare_threads;
114 extern int ap_max_spare_threads;
115 extern HMTX ap_mpm_accept_mutex;
117 static void worker_main(void *vpArg);
118 static void clean_child_exit(int code);
119 static void set_signals();
120 static void server_maintenance(void *vpArg);
123 static void clean_child_exit(int code)
126 apr_pool_destroy(pchild);
134 void ap_mpm_child_main(apr_pool_t *pconf)
136 ap_listen_rec *lr = NULL;
137 ap_listen_rec *first_lr = NULL;
138 int requests_this_child = 0;
139 apr_socket_t *sd = ap_listeners->sd;
141 unsigned long ulTimes;
142 int my_pid = getpid();
145 apr_pollfd_t *pollset;
147 TID server_maint_tid;
150 /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
151 DosSetSignalExceptionFocus(0, &ulTimes);
154 /* Create pool for child */
155 apr_pool_create(&pchild, pconf);
157 ap_run_child_init(pchild, ap_server_conf);
159 /* Create an event semaphore used to trigger other threads to shutdown */
160 rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
163 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
164 "unable to create shutdown semaphore, exiting");
165 clean_child_exit(APEXIT_CHILDFATAL);
168 /* Gain access to the scoreboard. */
169 rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname,
173 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
174 "scoreboard not readable in child, exiting");
175 clean_child_exit(APEXIT_CHILDFATAL);
178 ap_calc_scoreboard_size();
179 ap_init_scoreboard(sb_mem);
181 /* Gain access to the accpet mutex */
182 rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
185 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
186 "accept mutex couldn't be accessed in child, exiting");
187 clean_child_exit(APEXIT_CHILDFATAL);
190 /* Find our pid in the scoreboard so we know what slot our parent allocated us */
191 for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
193 if (child_slot == HARD_SERVER_LIMIT) {
194 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
195 "child pid not found in scoreboard, exiting");
196 clean_child_exit(APEXIT_CHILDFATAL);
199 ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
200 memset(ap_scoreboard_image->servers[child_slot], 0, sizeof(worker_score) * HARD_THREAD_LIMIT);
202 /* Set up an OS/2 queue for passing connections & termination requests
205 rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
208 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
209 "unable to create work queue, exiting");
210 clean_child_exit(APEXIT_CHILDFATAL);
213 /* Create initial pool of worker threads */
214 for (c = 0; c < ap_min_spare_threads; c++) {
215 // ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
218 /* Start maintenance thread */
219 server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
222 for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
226 apr_poll_setup(&pollset, num_listeners, pchild);
228 for (lr = ap_listeners; lr; lr = lr->next) {
229 apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
232 /* Main connection accept loop */
235 worker_args_t *worker_args;
237 apr_pool_create(&pconn, pchild);
238 worker_args = apr_palloc(pconn, sizeof(worker_args_t));
239 worker_args->pconn = pconn;
241 if (num_listeners == 1) {
242 rv = apr_socket_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
244 rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
246 if (shutdown_pending) {
247 DosReleaseMutexSem(ap_mpm_accept_mutex);
251 rv = APR_FROM_OS_ERROR(rc);
253 if (rv == APR_SUCCESS) {
254 rv = apr_poll(pollset, num_listeners, &nsds, -1);
255 DosReleaseMutexSem(ap_mpm_accept_mutex);
258 if (rv == APR_SUCCESS) {
259 if (first_lr == NULL) {
260 first_lr = ap_listeners;
268 apr_poll_revents_get(&event, lr->sd, pollset);
270 if (event == APR_POLLIN) {
273 apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
274 apr_sockaddr_port_get(&port, sa);
283 } while (lr != first_lr);
285 if (lr == first_lr) {
290 rv = apr_socket_accept(&worker_args->conn_sd, sd, pconn);
294 if (rv != APR_SUCCESS) {
295 if (!APR_STATUS_IS_EINTR(rv)) {
296 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
297 "apr_socket_accept");
298 clean_child_exit(APEXIT_CHILDFATAL);
301 DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
302 requests_this_child++;
305 if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
307 } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation);
309 ap_scoreboard_image->parent[child_slot].quiescing = 1;
310 DosPostEventSem(shutdown_event);
311 DosWaitThread(&server_maint_tid, DCWW_WAIT);
316 /* tell our worker threads to exit */
317 for (c=0; c<HARD_THREAD_LIMIT; c++) {
318 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
319 DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
326 for (c=0; c<HARD_THREAD_LIMIT; c++) {
327 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
335 DosPurgeQueue(workq);
337 for (c=0; c<HARD_THREAD_LIMIT; c++) {
338 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
339 DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
344 apr_pool_destroy(pchild);
353 /* Find a free thread slot */
354 for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
355 if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
356 ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
357 ap_scoreboard_image->servers[child_slot][thread_slot].tid =
358 _beginthread(worker_main, NULL, 128*1024, (void *)thread_slot);
366 ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
367 EXCEPTIONREGISTRATIONRECORD *pRegRec,
368 CONTEXTRECORD *pContext,
373 if (pReportRec->fHandlerFlags & EH_NESTED_CALL) {
374 return XCPT_CONTINUE_SEARCH;
377 if (pReportRec->ExceptionNum == XCPT_ACCESS_VIOLATION ||
378 pReportRec->ExceptionNum == XCPT_INTEGER_DIVIDE_BY_ZERO) {
379 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
380 "caught exception in worker thread, initiating child shutdown pid=%d", getpid());
381 for (c=0; c<HARD_THREAD_LIMIT; c++) {
382 if (ap_scoreboard_image->servers[child_slot][c].tid == _gettid()) {
383 ap_scoreboard_image->servers[child_slot][c].status = SERVER_DEAD;
388 /* Shut down process ASAP, it could be quite unhealthy & leaking resources */
389 shutdown_pending = 1;
390 ap_scoreboard_image->parent[child_slot].quiescing = 1;
391 kill(getpid(), SIGHUP);
392 DosUnwindException(UNWIND_ALL, 0, 0);
395 return XCPT_CONTINUE_SEARCH;
400 static void worker_main(void *vpArg)
403 conn_rec *current_conn;
405 apr_allocator_t *allocator;
406 apr_bucket_alloc_t *bucket_alloc;
407 worker_args_t *worker_args;
414 int thread_slot = (int)vpArg;
415 EXCEPTIONREGISTRATIONRECORD reg_rec = { NULL, thread_exception_handler };
418 /* Trap exceptions in this thread so we don't take down the whole process */
419 DosSetExceptionHandler( ®_rec );
421 rc = DosOpenQueue(&owner, &workq,
422 apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
425 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
426 "unable to open work queue, exiting");
427 ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
430 conn_id = ID_FROM_CHILD_THREAD(child_slot, thread_slot);
431 ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_READY,
434 apr_allocator_create(&allocator);
435 apr_allocator_max_free_set(allocator, ap_max_mem_free);
436 bucket_alloc = apr_bucket_alloc_create_ex(allocator);
438 while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
439 rc == 0 && rd.ulData != WORKTYPE_EXIT) {
440 pconn = worker_args->pconn;
441 ap_create_sb_handle(&sbh, pconn, child_slot, thread_slot);
442 current_conn = ap_run_create_connection(pconn, ap_server_conf,
443 worker_args->conn_sd, conn_id,
447 ap_process_connection(current_conn, worker_args->conn_sd);
448 ap_lingering_close(current_conn);
451 apr_pool_destroy(pconn);
452 ap_update_child_status_from_indexes(child_slot, thread_slot,
456 ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_DEAD,
459 apr_bucket_alloc_destroy(bucket_alloc);
460 apr_allocator_destroy(allocator);
465 static void server_maintenance(void *vpArg)
467 int num_idle, num_needed;
468 ULONG num_pending = 0;
474 rc = DosOpenQueue(&owner, &workq,
475 apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
478 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
479 "unable to open work queue in maintenance thread");
484 for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
485 num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
488 DosQueryQueue(workq, &num_pending);
489 num_needed = ap_min_spare_threads - num_idle + num_pending;
491 if (num_needed > 0) {
492 for (threadnum=0; threadnum < num_needed; threadnum++) {
497 if (num_idle - num_pending > ap_max_spare_threads) {
498 DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
500 } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
505 /* Signal handling routines */
507 static void sig_term(int sig)
509 shutdown_pending = 1;
511 signal(SIGTERM, SIG_DFL);
516 static void sig_hup(int sig)
518 shutdown_pending = 1;
524 static void set_signals()
528 sigemptyset(&sa.sa_mask);
530 sa.sa_handler = sig_term;
532 if (sigaction(SIGTERM, &sa, NULL) < 0)
533 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
535 sa.sa_handler = sig_hup;
537 if (sigaction(SIGHUP, &sa, NULL) < 0)
538 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");