bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / server / mpm / mpmt_os2 / mpmt_os2_child.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16  
17 #define CORE_PRIVATE
18 #define INCL_NOPMAPI
19 #define INCL_DOS
20 #define INCL_DOSERRORS
21
22 #include "ap_config.h"
23 #include "httpd.h"
24 #include "mpm_default.h"
25 #include "http_main.h"
26 #include "http_log.h"
27 #include "http_config.h"
28 #include "http_core.h"          /* for get_remote_host */
29 #include "http_connection.h"
30 #include "mpm.h"
31 #include "ap_mpm.h"
32 #include "ap_listen.h"
33 #include "apr_portable.h"
34 #include "apr_poll.h"
35 #include "mpm_common.h"
36 #include "apr_strings.h"
37 #include <os2.h>
38 #include <process.h>
39
40 /* XXXXXX move these to header file private to this MPM */
41
42 /* We don't need many processes, 
43  * they're only for redundancy in the event of a crash 
44  */
45 #define HARD_SERVER_LIMIT 10
46
47 /* Limit on the total number of threads per process
48  */
49 #ifndef HARD_THREAD_LIMIT
50 #define HARD_THREAD_LIMIT 256
51 #endif
52
53 #define ID_FROM_CHILD_THREAD(c, t)    ((c * HARD_THREAD_LIMIT) + t)
54
55 typedef struct {
56     apr_pool_t *pconn;
57     apr_socket_t *conn_sd;
58 } worker_args_t;
59
60 #define WORKTYPE_CONN 0
61 #define WORKTYPE_EXIT 1
62
63 static apr_pool_t *pchild = NULL;
64 static int child_slot;
65 static int shutdown_pending = 0;
66 extern int ap_my_generation;
67 static int volatile is_graceful = 1;
68 HEV shutdown_event; /* signaled when this child is shutting down */
69
70 /* grab some MPM globals */
71 extern int ap_min_spare_threads;
72 extern int ap_max_spare_threads;
73 extern HMTX ap_mpm_accept_mutex;
74
75 static void worker_main(void *vpArg);
76 static void clean_child_exit(int code);
77 static void set_signals();
78 static void server_maintenance(void *vpArg);
79
80
81 static void clean_child_exit(int code)
82 {
83     if (pchild) {
84         apr_pool_destroy(pchild);
85     }
86
87     exit(code);
88 }
89
90
91
92 void ap_mpm_child_main(apr_pool_t *pconf)
93 {
94     ap_listen_rec *lr = NULL;
95     ap_listen_rec *first_lr = NULL;
96     int requests_this_child = 0;
97     apr_socket_t *sd = ap_listeners->sd;
98     int nsds, rv = 0;
99     unsigned long ulTimes;
100     int my_pid = getpid();
101     ULONG rc, c;
102     HQUEUE workq;
103     apr_pollfd_t *pollset;
104     int num_listeners;
105     TID server_maint_tid;
106     void *sb_mem;
107
108     /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
109     DosSetSignalExceptionFocus(0, &ulTimes);
110     set_signals();
111
112     /* Create pool for child */
113     apr_pool_create(&pchild, pconf);
114
115     ap_run_child_init(pchild, ap_server_conf);
116
117     /* Create an event semaphore used to trigger other threads to shutdown */
118     rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
119
120     if (rc) {
121         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
122                      "unable to create shutdown semaphore, exiting");
123         clean_child_exit(APEXIT_CHILDFATAL);
124     }
125
126     /* Gain access to the scoreboard. */
127     rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname,
128                               PAG_READ|PAG_WRITE);
129
130     if (rc) {
131         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
132                      "scoreboard not readable in child, exiting");
133         clean_child_exit(APEXIT_CHILDFATAL);
134     }
135
136     ap_calc_scoreboard_size();
137     ap_init_scoreboard(sb_mem);
138
139     /* Gain access to the accpet mutex */
140     rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
141
142     if (rc) {
143         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
144                      "accept mutex couldn't be accessed in child, exiting");
145         clean_child_exit(APEXIT_CHILDFATAL);
146     }
147
148     /* Find our pid in the scoreboard so we know what slot our parent allocated us */
149     for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
150
151     if (child_slot == HARD_SERVER_LIMIT) {
152         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
153                      "child pid not found in scoreboard, exiting");
154         clean_child_exit(APEXIT_CHILDFATAL);
155     }
156
157     ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
158     memset(ap_scoreboard_image->servers[child_slot], 0, sizeof(worker_score) * HARD_THREAD_LIMIT);
159
160     /* Set up an OS/2 queue for passing connections & termination requests
161      * to worker threads
162      */
163     rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
164
165     if (rc) {
166         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
167                      "unable to create work queue, exiting");
168         clean_child_exit(APEXIT_CHILDFATAL);
169     }
170
171     /* Create initial pool of worker threads */
172     for (c = 0; c < ap_min_spare_threads; c++) {
173 //        ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
174     }
175
176     /* Start maintenance thread */
177     server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
178
179     /* Set up poll */
180     for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
181         num_listeners++;
182     }
183
184     apr_poll_setup(&pollset, num_listeners, pchild);
185
186     for (lr = ap_listeners; lr; lr = lr->next) {
187         apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
188     }
189
190     /* Main connection accept loop */
191     do {
192         apr_pool_t *pconn;
193         worker_args_t *worker_args;
194
195         apr_pool_create(&pconn, pchild);
196         worker_args = apr_palloc(pconn, sizeof(worker_args_t));
197         worker_args->pconn = pconn;
198
199         if (num_listeners == 1) {
200             rv = apr_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
201         } else {
202             rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
203
204             if (shutdown_pending) {
205                 DosReleaseMutexSem(ap_mpm_accept_mutex);
206                 break;
207             }
208
209             rv = APR_FROM_OS_ERROR(rc);
210
211             if (rv == APR_SUCCESS) {
212                 rv = apr_poll(pollset, num_listeners, &nsds, -1);
213                 DosReleaseMutexSem(ap_mpm_accept_mutex);
214             }
215
216             if (rv == APR_SUCCESS) {
217                 if (first_lr == NULL) {
218                     first_lr = ap_listeners;
219                 }
220
221                 lr = first_lr;
222
223                 do {
224                     apr_int16_t event;
225
226                     apr_poll_revents_get(&event, lr->sd, pollset);
227
228                     if (event == APR_POLLIN) {
229                         apr_sockaddr_t *sa;
230                         apr_port_t port;
231                         apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
232                         apr_sockaddr_port_get(&port, sa);
233                         first_lr = lr->next;
234                         break;
235                     }
236                     lr = lr->next;
237
238                     if (!lr) {
239                         lr = ap_listeners;
240                     }
241                 } while (lr != first_lr);
242
243                 if (lr == first_lr) {
244                     continue;
245                 }
246
247                 sd = lr->sd;
248                 rv = apr_accept(&worker_args->conn_sd, sd, pconn);
249             }
250         }
251
252         if (rv != APR_SUCCESS) {
253             if (!APR_STATUS_IS_EINTR(rv)) {
254                 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
255                              "apr_accept");
256                 clean_child_exit(APEXIT_CHILDFATAL);
257             }
258         } else {
259             DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
260             requests_this_child++;
261         }
262
263         if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
264             break;
265     } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation);
266
267     ap_scoreboard_image->parent[child_slot].quiescing = 1;
268     DosPostEventSem(shutdown_event);
269     DosWaitThread(&server_maint_tid, DCWW_WAIT);
270
271     if (is_graceful) {
272         char someleft;
273
274         /* tell our worker threads to exit */
275         for (c=0; c<HARD_THREAD_LIMIT; c++) {
276             if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
277                 DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
278             }
279         }
280
281         do {
282             someleft = 0;
283
284             for (c=0; c<HARD_THREAD_LIMIT; c++) {
285                 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
286                     someleft = 1;
287                     DosSleep(1000);
288                     break;
289                 }
290             }
291         } while (someleft);
292     } else {
293         DosPurgeQueue(workq);
294
295         for (c=0; c<HARD_THREAD_LIMIT; c++) {
296             if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
297                 DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
298             }
299         }
300     }
301
302     apr_pool_destroy(pchild);
303 }
304
305
306
307 void add_worker()
308 {
309     int thread_slot;
310
311     /* Find a free thread slot */
312     for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
313         if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
314             ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
315             ap_scoreboard_image->servers[child_slot][thread_slot].tid =
316                 _beginthread(worker_main, NULL, 128*1024, (void *)thread_slot);
317             break;
318         }
319     }
320 }
321
322
323
324 ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
325                                         EXCEPTIONREGISTRATIONRECORD *pRegRec,
326                                         CONTEXTRECORD *pContext,
327                                         PVOID p)
328 {
329     int c;
330
331     if (pReportRec->fHandlerFlags & EH_NESTED_CALL) {
332         return XCPT_CONTINUE_SEARCH;
333     }
334
335     if (pReportRec->ExceptionNum == XCPT_ACCESS_VIOLATION ||
336         pReportRec->ExceptionNum == XCPT_INTEGER_DIVIDE_BY_ZERO) {
337         ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
338                      "caught exception in worker thread, initiating child shutdown pid=%d", getpid());
339         for (c=0; c<HARD_THREAD_LIMIT; c++) {
340             if (ap_scoreboard_image->servers[child_slot][c].tid == _gettid()) {
341                 ap_scoreboard_image->servers[child_slot][c].status = SERVER_DEAD;
342                 break;
343             }
344         }
345
346         /* Shut down process ASAP, it could be quite unhealthy & leaking resources */
347         shutdown_pending = 1;
348         ap_scoreboard_image->parent[child_slot].quiescing = 1;
349         kill(getpid(), SIGHUP);
350         DosUnwindException(UNWIND_ALL, 0, 0);
351     }
352   
353     return XCPT_CONTINUE_SEARCH;
354 }
355
356
357
358 static void worker_main(void *vpArg)
359 {
360     long conn_id;
361     conn_rec *current_conn;
362     apr_pool_t *pconn;
363     apr_allocator_t *allocator;
364     apr_bucket_alloc_t *bucket_alloc;
365     worker_args_t *worker_args;
366     HQUEUE workq;
367     PID owner;
368     int rc;
369     REQUESTDATA rd;
370     ULONG len;
371     BYTE priority;
372     int thread_slot = (int)vpArg;
373     EXCEPTIONREGISTRATIONRECORD reg_rec = { NULL, thread_exception_handler };
374     ap_sb_handle_t *sbh;
375   
376     /* Trap exceptions in this thread so we don't take down the whole process */
377     DosSetExceptionHandler( &reg_rec );
378
379     rc = DosOpenQueue(&owner, &workq,
380                       apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
381
382     if (rc) {
383         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
384                      "unable to open work queue, exiting");
385         ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
386     }
387
388     conn_id = ID_FROM_CHILD_THREAD(child_slot, thread_slot);
389     ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_READY, 
390                                         NULL);
391
392     apr_allocator_create(&allocator);
393     apr_allocator_max_free_set(allocator, ap_max_mem_free);
394     bucket_alloc = apr_bucket_alloc_create_ex(allocator);
395
396     while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
397            rc == 0 && rd.ulData != WORKTYPE_EXIT) {
398         pconn = worker_args->pconn;
399         ap_create_sb_handle(&sbh, pconn, child_slot, thread_slot);
400         current_conn = ap_run_create_connection(pconn, ap_server_conf,
401                                                 worker_args->conn_sd, conn_id,
402                                                 sbh, bucket_alloc);
403
404         if (current_conn) {
405             ap_process_connection(current_conn, worker_args->conn_sd);
406             ap_lingering_close(current_conn);
407         }
408
409         apr_pool_destroy(pconn);
410         ap_update_child_status_from_indexes(child_slot, thread_slot, 
411                                             SERVER_READY, NULL);
412     }
413
414     ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_DEAD, 
415                                         NULL);
416
417     apr_bucket_alloc_destroy(bucket_alloc);
418     apr_allocator_destroy(allocator);
419 }
420
421
422
423 static void server_maintenance(void *vpArg)
424 {
425     int num_idle, num_needed;
426     ULONG num_pending = 0;
427     int threadnum;
428     HQUEUE workq;
429     ULONG rc;
430     PID owner;
431
432     rc = DosOpenQueue(&owner, &workq,
433                       apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
434
435     if (rc) {
436         ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
437                      "unable to open work queue in maintenance thread");
438         return;
439     }
440
441     do {
442         for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
443             num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
444         }
445
446         DosQueryQueue(workq, &num_pending);
447         num_needed = ap_min_spare_threads - num_idle + num_pending;
448
449         if (num_needed > 0) {
450             for (threadnum=0; threadnum < num_needed; threadnum++) {
451                 add_worker();
452             }
453         }
454
455         if (num_idle - num_pending > ap_max_spare_threads) {
456             DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
457         }
458     } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
459 }
460
461
462
463 /* Signal handling routines */
464
465 static void sig_term(int sig)
466 {
467     shutdown_pending = 1;
468     is_graceful = 0;
469     signal(SIGTERM, SIG_DFL);
470 }
471
472
473
474 static void sig_hup(int sig)
475 {
476     shutdown_pending = 1;
477     is_graceful = 1;
478 }
479
480
481
482 static void set_signals()
483 {
484     struct sigaction sa;
485
486     sigemptyset(&sa.sa_mask);
487     sa.sa_flags = 0;
488     sa.sa_handler = sig_term;
489
490     if (sigaction(SIGTERM, &sa, NULL) < 0)
491         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
492
493     sa.sa_handler = sig_hup;
494
495     if (sigaction(SIGHUP, &sa, NULL) < 0)
496         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
497 }