~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to server/mpm/mpmt_os2/mpmt_os2_child.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

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