]> granicus.if.org Git - apache/blob - server/mpm/mpmt_os2/mpmt_os2_child.c
switch to APR 1.0 API (which is still in flux)
[apache] / server / mpm / mpmt_os2 / mpmt_os2_child.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
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
17  *    distribution.
18  *
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.
25  *
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.
30  *
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.
34  *
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
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
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/>.
53  *
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.
57  */
58  
59 #define CORE_PRIVATE
60 #define INCL_NOPMAPI
61 #define INCL_DOS
62 #define INCL_DOSERRORS
63
64 #include "ap_config.h"
65 #include "httpd.h"
66 #include "mpm_default.h"
67 #include "http_main.h"
68 #include "http_log.h"
69 #include "http_config.h"
70 #include "http_core.h"          /* for get_remote_host */
71 #include "http_connection.h"
72 #include "mpm.h"
73 #include "ap_mpm.h"
74 #include "ap_listen.h"
75 #include "apr_portable.h"
76 #include "apr_poll.h"
77 #include "mpm_common.h"
78 #include "apr_strings.h"
79 #include <os2.h>
80 #include <process.h>
81
82 /* XXXXXX move these to header file private to this MPM */
83
84 /* We don't need many processes, 
85  * they're only for redundancy in the event of a crash 
86  */
87 #define HARD_SERVER_LIMIT 10
88
89 /* Limit on the total number of threads per process
90  */
91 #ifndef HARD_THREAD_LIMIT
92 #define HARD_THREAD_LIMIT 256
93 #endif
94
95 #define ID_FROM_CHILD_THREAD(c, t)    ((c * HARD_THREAD_LIMIT) + t)
96
97 typedef struct {
98     apr_pool_t *pconn;
99     apr_socket_t *conn_sd;
100 } worker_args_t;
101
102 #define WORKTYPE_CONN 0
103 #define WORKTYPE_EXIT 1
104
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 */
111
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;
116
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);
121
122
123 static void clean_child_exit(int code)
124 {
125     if (pchild) {
126         apr_pool_destroy(pchild);
127     }
128
129     exit(code);
130 }
131
132
133
134 void ap_mpm_child_main(apr_pool_t *pconf)
135 {
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;
140     int nsds, rv = 0;
141     unsigned long ulTimes;
142     int my_pid = getpid();
143     ULONG rc, c;
144     HQUEUE workq;
145     apr_pollfd_t *pollset;
146     int num_listeners;
147     TID server_maint_tid;
148     void *sb_mem;
149
150     /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
151     DosSetSignalExceptionFocus(0, &ulTimes);
152     set_signals();
153
154     /* Create pool for child */
155     apr_pool_create(&pchild, pconf);
156
157     ap_run_child_init(pchild, ap_server_conf);
158
159     /* Create an event semaphore used to trigger other threads to shutdown */
160     rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
161
162     if (rc) {
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);
166     }
167
168     /* Gain access to the scoreboard. */
169     rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname,
170                               PAG_READ|PAG_WRITE);
171
172     if (rc) {
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);
176     }
177
178     ap_calc_scoreboard_size();
179     ap_init_scoreboard(sb_mem);
180
181     /* Gain access to the accpet mutex */
182     rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
183
184     if (rc) {
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);
188     }
189
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++);
192
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);
197     }
198
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);
201
202     /* Set up an OS/2 queue for passing connections & termination requests
203      * to worker threads
204      */
205     rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
206
207     if (rc) {
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);
211     }
212
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);
216     }
217
218     /* Start maintenance thread */
219     server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
220
221     /* Set up poll */
222     for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
223         num_listeners++;
224     }
225
226     apr_poll_setup(&pollset, num_listeners, pchild);
227
228     for (lr = ap_listeners; lr; lr = lr->next) {
229         apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
230     }
231
232     /* Main connection accept loop */
233     do {
234         apr_pool_t *pconn;
235         worker_args_t *worker_args;
236
237         apr_pool_create(&pconn, pchild);
238         worker_args = apr_palloc(pconn, sizeof(worker_args_t));
239         worker_args->pconn = pconn;
240
241         if (num_listeners == 1) {
242             rv = apr_socket_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
243         } else {
244             rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
245
246             if (shutdown_pending) {
247                 DosReleaseMutexSem(ap_mpm_accept_mutex);
248                 break;
249             }
250
251             rv = APR_FROM_OS_ERROR(rc);
252
253             if (rv == APR_SUCCESS) {
254                 rv = apr_poll(pollset, num_listeners, &nsds, -1);
255                 DosReleaseMutexSem(ap_mpm_accept_mutex);
256             }
257
258             if (rv == APR_SUCCESS) {
259                 if (first_lr == NULL) {
260                     first_lr = ap_listeners;
261                 }
262
263                 lr = first_lr;
264
265                 do {
266                     apr_int16_t event;
267
268                     apr_poll_revents_get(&event, lr->sd, pollset);
269
270                     if (event == APR_POLLIN) {
271                         apr_sockaddr_t *sa;
272                         apr_port_t port;
273                         apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
274                         apr_sockaddr_port_get(&port, sa);
275                         first_lr = lr->next;
276                         break;
277                     }
278                     lr = lr->next;
279
280                     if (!lr) {
281                         lr = ap_listeners;
282                     }
283                 } while (lr != first_lr);
284
285                 if (lr == first_lr) {
286                     continue;
287                 }
288
289                 sd = lr->sd;
290                 rv = apr_socket_accept(&worker_args->conn_sd, sd, pconn);
291             }
292         }
293
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);
299             }
300         } else {
301             DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
302             requests_this_child++;
303         }
304
305         if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
306             break;
307     } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation);
308
309     ap_scoreboard_image->parent[child_slot].quiescing = 1;
310     DosPostEventSem(shutdown_event);
311     DosWaitThread(&server_maint_tid, DCWW_WAIT);
312
313     if (is_graceful) {
314         char someleft;
315
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);
320             }
321         }
322
323         do {
324             someleft = 0;
325
326             for (c=0; c<HARD_THREAD_LIMIT; c++) {
327                 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
328                     someleft = 1;
329                     DosSleep(1000);
330                     break;
331                 }
332             }
333         } while (someleft);
334     } else {
335         DosPurgeQueue(workq);
336
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);
340             }
341         }
342     }
343
344     apr_pool_destroy(pchild);
345 }
346
347
348
349 void add_worker()
350 {
351     int thread_slot;
352
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);
359             break;
360         }
361     }
362 }
363
364
365
366 ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
367                                         EXCEPTIONREGISTRATIONRECORD *pRegRec,
368                                         CONTEXTRECORD *pContext,
369                                         PVOID p)
370 {
371     int c;
372
373     if (pReportRec->fHandlerFlags & EH_NESTED_CALL) {
374         return XCPT_CONTINUE_SEARCH;
375     }
376
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;
384                 break;
385             }
386         }
387
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);
393     }
394   
395     return XCPT_CONTINUE_SEARCH;
396 }
397
398
399
400 static void worker_main(void *vpArg)
401 {
402     long conn_id;
403     conn_rec *current_conn;
404     apr_pool_t *pconn;
405     apr_allocator_t *allocator;
406     apr_bucket_alloc_t *bucket_alloc;
407     worker_args_t *worker_args;
408     HQUEUE workq;
409     PID owner;
410     int rc;
411     REQUESTDATA rd;
412     ULONG len;
413     BYTE priority;
414     int thread_slot = (int)vpArg;
415     EXCEPTIONREGISTRATIONRECORD reg_rec = { NULL, thread_exception_handler };
416     ap_sb_handle_t *sbh;
417   
418     /* Trap exceptions in this thread so we don't take down the whole process */
419     DosSetExceptionHandler( &reg_rec );
420
421     rc = DosOpenQueue(&owner, &workq,
422                       apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
423
424     if (rc) {
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;
428     }
429
430     conn_id = ID_FROM_CHILD_THREAD(child_slot, thread_slot);
431     ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_READY, 
432                                         NULL);
433
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);
437
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,
444                                                 sbh, bucket_alloc);
445
446         if (current_conn) {
447             ap_process_connection(current_conn, worker_args->conn_sd);
448             ap_lingering_close(current_conn);
449         }
450
451         apr_pool_destroy(pconn);
452         ap_update_child_status_from_indexes(child_slot, thread_slot, 
453                                             SERVER_READY, NULL);
454     }
455
456     ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_DEAD, 
457                                         NULL);
458
459     apr_bucket_alloc_destroy(bucket_alloc);
460     apr_allocator_destroy(allocator);
461 }
462
463
464
465 static void server_maintenance(void *vpArg)
466 {
467     int num_idle, num_needed;
468     ULONG num_pending = 0;
469     int threadnum;
470     HQUEUE workq;
471     ULONG rc;
472     PID owner;
473
474     rc = DosOpenQueue(&owner, &workq,
475                       apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
476
477     if (rc) {
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");
480         return;
481     }
482
483     do {
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;
486         }
487
488         DosQueryQueue(workq, &num_pending);
489         num_needed = ap_min_spare_threads - num_idle + num_pending;
490
491         if (num_needed > 0) {
492             for (threadnum=0; threadnum < num_needed; threadnum++) {
493                 add_worker();
494             }
495         }
496
497         if (num_idle - num_pending > ap_max_spare_threads) {
498             DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
499         }
500     } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
501 }
502
503
504
505 /* Signal handling routines */
506
507 static void sig_term(int sig)
508 {
509     shutdown_pending = 1;
510     is_graceful = 0;
511     signal(SIGTERM, SIG_DFL);
512 }
513
514
515
516 static void sig_hup(int sig)
517 {
518     shutdown_pending = 1;
519     is_graceful = 1;
520 }
521
522
523
524 static void set_signals()
525 {
526     struct sigaction sa;
527
528     sigemptyset(&sa.sa_mask);
529     sa.sa_flags = 0;
530     sa.sa_handler = sig_term;
531
532     if (sigaction(SIGTERM, &sa, NULL) < 0)
533         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
534
535     sa.sa_handler = sig_hup;
536
537     if (sigaction(SIGHUP, &sa, NULL) < 0)
538         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
539 }