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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #define INCL_DOSERRORS
21 #include "ap_config.h"
23 #include "mpm_default.h"
24 #include "http_main.h"
26 #include "http_config.h"
27 #include "http_core.h" /* for get_remote_host */
28 #include "http_connection.h"
29 #include "scoreboard.h"
31 #include "ap_listen.h"
32 #include "apr_portable.h"
34 #include "mpm_common.h"
35 #include "apr_strings.h"
39 APLOG_USE_MODULE(mpm_mpmt_os2);
41 /* XXXXXX move these to header file private to this MPM */
43 /* We don't need many processes,
44 * they're only for redundancy in the event of a crash
46 #define HARD_SERVER_LIMIT 10
48 /* Limit on the total number of threads per process
50 #ifndef HARD_THREAD_LIMIT
51 #define HARD_THREAD_LIMIT 256
54 #define ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t)
58 apr_socket_t *conn_sd;
61 #define WORKTYPE_CONN 0
62 #define WORKTYPE_EXIT 1
64 static apr_pool_t *pchild = NULL;
65 static int child_slot;
66 static int shutdown_pending = 0;
67 extern int ap_my_generation;
68 static int volatile is_graceful = 1;
69 HEV shutdown_event; /* signaled when this child is shutting down */
71 /* grab some MPM globals */
72 extern int ap_min_spare_threads;
73 extern int ap_max_spare_threads;
74 extern HMTX ap_mpm_accept_mutex;
76 static void worker_main(void *vpArg);
77 static void clean_child_exit(int code);
78 static void set_signals();
79 static void server_maintenance(void *vpArg);
82 static void clean_child_exit(int code)
85 apr_pool_destroy(pchild);
93 void ap_mpm_child_main(apr_pool_t *pconf)
95 ap_listen_rec *lr = NULL;
96 int requests_this_child = 0;
98 unsigned long ulTimes;
99 int my_pid = getpid();
102 apr_pollset_t *pollset;
104 TID server_maint_tid;
107 /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
108 DosSetSignalExceptionFocus(0, &ulTimes);
111 /* Create pool for child */
112 apr_pool_create(&pchild, pconf);
114 ap_run_child_init(pchild, ap_server_conf);
116 /* Create an event semaphore used to trigger other threads to shutdown */
117 rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
120 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00189)
121 "unable to create shutdown semaphore, exiting");
122 clean_child_exit(APEXIT_CHILDFATAL);
125 /* Gain access to the scoreboard. */
126 rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname,
130 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00190)
131 "scoreboard not readable in child, exiting");
132 clean_child_exit(APEXIT_CHILDFATAL);
135 ap_calc_scoreboard_size();
136 ap_init_scoreboard(sb_mem);
138 /* Gain access to the accpet mutex */
139 rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
142 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00191)
143 "accept mutex couldn't be accessed in child, exiting");
144 clean_child_exit(APEXIT_CHILDFATAL);
147 /* Find our pid in the scoreboard so we know what slot our parent allocated us */
148 for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
150 if (child_slot == HARD_SERVER_LIMIT) {
151 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00192)
152 "child pid not found in scoreboard, exiting");
153 clean_child_exit(APEXIT_CHILDFATAL);
156 ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
157 memset(ap_scoreboard_image->servers[child_slot], 0, sizeof(worker_score) * HARD_THREAD_LIMIT);
159 /* Set up an OS/2 queue for passing connections & termination requests
162 rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
165 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00193)
166 "unable to create work queue, exiting");
167 clean_child_exit(APEXIT_CHILDFATAL);
170 /* Create initial pool of worker threads */
171 for (c = 0; c < ap_min_spare_threads; c++) {
172 // ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
175 /* Start maintenance thread */
176 server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
179 for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
183 apr_pollset_create(&pollset, num_listeners, pchild, 0);
185 for (lr = ap_listeners; lr != NULL; lr = lr->next) {
186 apr_pollfd_t pfd = { 0 };
188 pfd.desc_type = APR_POLL_SOCKET;
190 pfd.reqevents = APR_POLLIN;
191 pfd.client_data = lr;
192 apr_pollset_add(pollset, &pfd);
195 /* Main connection accept loop */
198 worker_args_t *worker_args;
199 int last_poll_idx = 0;
201 apr_pool_create(&pconn, pchild);
202 worker_args = apr_palloc(pconn, sizeof(worker_args_t));
203 worker_args->pconn = pconn;
205 if (num_listeners == 1) {
206 rv = apr_socket_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
208 const apr_pollfd_t *poll_results;
209 apr_int32_t num_poll_results;
211 rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
213 if (shutdown_pending) {
214 DosReleaseMutexSem(ap_mpm_accept_mutex);
218 rv = APR_FROM_OS_ERROR(rc);
220 if (rv == APR_SUCCESS) {
221 rv = apr_pollset_poll(pollset, -1, &num_poll_results, &poll_results);
222 DosReleaseMutexSem(ap_mpm_accept_mutex);
225 if (rv == APR_SUCCESS) {
226 if (last_poll_idx >= num_listeners) {
230 lr = poll_results[last_poll_idx++].client_data;
231 rv = apr_socket_accept(&worker_args->conn_sd, lr->sd, pconn);
236 if (rv != APR_SUCCESS) {
237 if (!APR_STATUS_IS_EINTR(rv)) {
238 ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00194)
239 "apr_socket_accept");
240 clean_child_exit(APEXIT_CHILDFATAL);
243 DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
244 requests_this_child++;
247 if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
249 } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation);
251 ap_scoreboard_image->parent[child_slot].quiescing = 1;
252 DosPostEventSem(shutdown_event);
253 DosWaitThread(&server_maint_tid, DCWW_WAIT);
258 /* tell our worker threads to exit */
259 for (c=0; c<HARD_THREAD_LIMIT; c++) {
260 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
261 DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
268 for (c=0; c<HARD_THREAD_LIMIT; c++) {
269 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
277 DosPurgeQueue(workq);
279 for (c=0; c<HARD_THREAD_LIMIT; c++) {
280 if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
281 DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
286 apr_pool_destroy(pchild);
294 int stacksize = ap_thread_stacksize == 0 ? 128*1024 : ap_thread_stacksize;
296 /* Find a free thread slot */
297 for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
298 if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
299 ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
300 ap_scoreboard_image->servers[child_slot][thread_slot].tid =
301 _beginthread(worker_main, NULL, stacksize, (void *)thread_slot);
309 ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
310 EXCEPTIONREGISTRATIONRECORD *pRegRec,
311 CONTEXTRECORD *pContext,
316 if (pReportRec->fHandlerFlags & EH_NESTED_CALL) {
317 return XCPT_CONTINUE_SEARCH;
320 if (pReportRec->ExceptionNum == XCPT_ACCESS_VIOLATION ||
321 pReportRec->ExceptionNum == XCPT_INTEGER_DIVIDE_BY_ZERO) {
322 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00195)
323 "caught exception in worker thread, initiating child shutdown pid=%d", getpid());
324 for (c=0; c<HARD_THREAD_LIMIT; c++) {
325 if (ap_scoreboard_image->servers[child_slot][c].tid == _gettid()) {
326 ap_scoreboard_image->servers[child_slot][c].status = SERVER_DEAD;
331 /* Shut down process ASAP, it could be quite unhealthy & leaking resources */
332 shutdown_pending = 1;
333 ap_scoreboard_image->parent[child_slot].quiescing = 1;
334 kill(getpid(), SIGHUP);
335 DosUnwindException(UNWIND_ALL, 0, 0);
338 return XCPT_CONTINUE_SEARCH;
343 static void worker_main(void *vpArg)
345 apr_thread_t *thd = NULL;
346 apr_os_thread_t osthd;
348 conn_rec *current_conn;
350 apr_allocator_t *allocator;
351 apr_bucket_alloc_t *bucket_alloc;
352 worker_args_t *worker_args;
359 int thread_slot = (int)vpArg;
360 EXCEPTIONREGISTRATIONRECORD reg_rec = { NULL, thread_exception_handler };
363 /* Trap exceptions in this thread so we don't take down the whole process */
364 DosSetExceptionHandler( ®_rec );
366 osthd = apr_os_thread_current();
367 apr_os_thread_put(&thd, &osthd, pchild);
369 rc = DosOpenQueue(&owner, &workq,
370 apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
373 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00196)
374 "unable to open work queue, exiting");
375 ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
378 conn_id = ID_FROM_CHILD_THREAD(child_slot, thread_slot);
379 ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_READY,
382 apr_allocator_create(&allocator);
383 apr_allocator_max_free_set(allocator, ap_max_mem_free);
384 bucket_alloc = apr_bucket_alloc_create_ex(allocator);
386 while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
387 rc == 0 && rd.ulData != WORKTYPE_EXIT) {
388 pconn = worker_args->pconn;
389 ap_create_sb_handle(&sbh, pconn, child_slot, thread_slot);
390 current_conn = ap_run_create_connection(pconn, ap_server_conf,
391 worker_args->conn_sd, conn_id,
395 current_conn->current_thread = thd;
396 ap_process_connection(current_conn, worker_args->conn_sd);
397 ap_lingering_close(current_conn);
400 apr_pool_destroy(pconn);
401 ap_update_child_status_from_indexes(child_slot, thread_slot,
405 ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_DEAD,
408 apr_bucket_alloc_destroy(bucket_alloc);
409 apr_allocator_destroy(allocator);
414 static void server_maintenance(void *vpArg)
416 int num_idle, num_needed;
417 ULONG num_pending = 0;
423 rc = DosOpenQueue(&owner, &workq,
424 apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
427 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00197)
428 "unable to open work queue in maintenance thread");
433 for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
434 num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
437 DosQueryQueue(workq, &num_pending);
438 num_needed = ap_min_spare_threads - num_idle + num_pending;
440 if (num_needed > 0) {
441 for (threadnum=0; threadnum < num_needed; threadnum++) {
446 if (num_idle - num_pending > ap_max_spare_threads) {
447 DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
449 } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
454 /* Signal handling routines */
456 static void sig_term(int sig)
458 shutdown_pending = 1;
460 signal(SIGTERM, SIG_DFL);
465 static void sig_hup(int sig)
467 shutdown_pending = 1;
473 static void set_signals()
477 sigemptyset(&sa.sa_mask);
479 sa.sa_handler = sig_term;
481 if (sigaction(SIGTERM, &sa, NULL) < 0)
482 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00198) "sigaction(SIGTERM)");
484 sa.sa_handler = sig_hup;
486 if (sigaction(SIGHUP, &sa, NULL) < 0)
487 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00199) "sigaction(SIGHUP)");