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