summaryrefslogtreecommitdiffstats
path: root/server/mpm/mpmt_os2/mpmt_os2_child.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/mpm/mpmt_os2/mpmt_os2_child.c')
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2_child.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c
new file mode 100644
index 0000000..f405cd2
--- /dev/null
+++ b/server/mpm/mpmt_os2/mpmt_os2_child.c
@@ -0,0 +1,490 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define INCL_NOPMAPI
+#define INCL_DOS
+#define INCL_DOSERRORS
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "mpm_default.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h"
+#include "http_core.h" /* for get_remote_host */
+#include "http_connection.h"
+#include "scoreboard.h"
+#include "ap_mpm.h"
+#include "ap_listen.h"
+#include "apr_portable.h"
+#include "apr_poll.h"
+#include "mpm_common.h"
+#include "apr_strings.h"
+#include <os2.h>
+#include <process.h>
+
+APLOG_USE_MODULE(mpm_mpmt_os2);
+
+/* XXXXXX move these to header file private to this MPM */
+
+/* We don't need many processes,
+ * they're only for redundancy in the event of a crash
+ */
+#define HARD_SERVER_LIMIT 10
+
+/* Limit on the total number of threads per process
+ */
+#ifndef HARD_THREAD_LIMIT
+#define HARD_THREAD_LIMIT 256
+#endif
+
+#define ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t)
+
+typedef struct {
+ apr_pool_t *pconn;
+ apr_socket_t *conn_sd;
+} worker_args_t;
+
+#define WORKTYPE_CONN 0
+#define WORKTYPE_EXIT 1
+
+static apr_pool_t *pchild = NULL;
+static int child_slot;
+static int shutdown_pending = 0;
+extern int ap_my_generation;
+static int volatile is_graceful = 1;
+HEV shutdown_event; /* signaled when this child is shutting down */
+
+/* grab some MPM globals */
+extern int ap_min_spare_threads;
+extern int ap_max_spare_threads;
+extern HMTX ap_mpm_accept_mutex;
+
+static void worker_main(void *vpArg);
+static void clean_child_exit(int code);
+static void set_signals();
+static void server_maintenance(void *vpArg);
+
+
+static void clean_child_exit(int code)
+{
+ if (pchild) {
+ apr_pool_destroy(pchild);
+ }
+
+ exit(code);
+}
+
+
+
+void ap_mpm_child_main(apr_pool_t *pconf)
+{
+ ap_listen_rec *lr = NULL;
+ int requests_this_child = 0;
+ int rv = 0;
+ unsigned long ulTimes;
+ int my_pid = getpid();
+ ULONG rc, c;
+ HQUEUE workq;
+ apr_pollset_t *pollset;
+ int num_listeners;
+ TID server_maint_tid;
+ void *sb_mem;
+
+ /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
+ DosSetSignalExceptionFocus(0, &ulTimes);
+ set_signals();
+
+ /* Create pool for child */
+ apr_pool_create(&pchild, pconf);
+ apr_pool_tag(pchild, "pchild");
+
+ ap_run_child_init(pchild, ap_server_conf);
+
+ /* Create an event semaphore used to trigger other threads to shutdown */
+ rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
+
+ if (rc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00189)
+ "unable to create shutdown semaphore, exiting");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ /* Gain access to the scoreboard. */
+ rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname,
+ PAG_READ|PAG_WRITE);
+
+ if (rc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00190)
+ "scoreboard not readable in child, exiting");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ ap_calc_scoreboard_size();
+ ap_init_scoreboard(sb_mem);
+
+ /* Gain access to the accpet mutex */
+ rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
+
+ if (rc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00191)
+ "accept mutex couldn't be accessed in child, exiting");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ /* Find our pid in the scoreboard so we know what slot our parent allocated us */
+ for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
+
+ if (child_slot == HARD_SERVER_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00192)
+ "child pid not found in scoreboard, exiting");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
+ memset(ap_scoreboard_image->servers[child_slot], 0, sizeof(worker_score) * HARD_THREAD_LIMIT);
+
+ /* Set up an OS/2 queue for passing connections & termination requests
+ * to worker threads
+ */
+ rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
+
+ if (rc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00193)
+ "unable to create work queue, exiting");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ /* Create initial pool of worker threads */
+ for (c = 0; c < ap_min_spare_threads; c++) {
+// ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
+ }
+
+ /* Start maintenance thread */
+ server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
+
+ /* Set up poll */
+ for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
+ num_listeners++;
+ }
+
+ apr_pollset_create(&pollset, num_listeners, pchild, 0);
+
+ for (lr = ap_listeners; lr != NULL; lr = lr->next) {
+ apr_pollfd_t pfd = { 0 };
+
+ pfd.desc_type = APR_POLL_SOCKET;
+ pfd.desc.s = lr->sd;
+ pfd.reqevents = APR_POLLIN;
+ pfd.client_data = lr;
+ apr_pollset_add(pollset, &pfd);
+ }
+
+ /* Main connection accept loop */
+ do {
+ apr_pool_t *pconn;
+ worker_args_t *worker_args;
+ int last_poll_idx = 0;
+
+ apr_pool_create(&pconn, pchild);
+ apr_pool_tag(pconn, "transaction");
+ worker_args = apr_palloc(pconn, sizeof(worker_args_t));
+ worker_args->pconn = pconn;
+
+ if (num_listeners == 1) {
+ rv = apr_socket_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
+ } else {
+ const apr_pollfd_t *poll_results;
+ apr_int32_t num_poll_results;
+
+ rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
+
+ if (shutdown_pending) {
+ DosReleaseMutexSem(ap_mpm_accept_mutex);
+ break;
+ }
+
+ rv = APR_FROM_OS_ERROR(rc);
+
+ if (rv == APR_SUCCESS) {
+ rv = apr_pollset_poll(pollset, -1, &num_poll_results, &poll_results);
+ DosReleaseMutexSem(ap_mpm_accept_mutex);
+ }
+
+ if (rv == APR_SUCCESS) {
+ if (last_poll_idx >= num_listeners) {
+ last_poll_idx = 0;
+ }
+
+ lr = poll_results[last_poll_idx++].client_data;
+ rv = apr_socket_accept(&worker_args->conn_sd, lr->sd, pconn);
+ last_poll_idx++;
+ }
+ }
+
+ if (rv != APR_SUCCESS) {
+ if (!APR_STATUS_IS_EINTR(rv)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00194)
+ "apr_socket_accept");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ } else {
+ DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
+ requests_this_child++;
+ }
+
+ if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
+ break;
+ } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation);
+
+ ap_scoreboard_image->parent[child_slot].quiescing = 1;
+ DosPostEventSem(shutdown_event);
+ DosWaitThread(&server_maint_tid, DCWW_WAIT);
+
+ if (is_graceful) {
+ char someleft;
+
+ /* tell our worker threads to exit */
+ for (c=0; c<HARD_THREAD_LIMIT; c++) {
+ if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
+ DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
+ }
+ }
+
+ do {
+ someleft = 0;
+
+ for (c=0; c<HARD_THREAD_LIMIT; c++) {
+ if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
+ someleft = 1;
+ DosSleep(1000);
+ break;
+ }
+ }
+ } while (someleft);
+ } else {
+ DosPurgeQueue(workq);
+
+ for (c=0; c<HARD_THREAD_LIMIT; c++) {
+ if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
+ DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
+ }
+ }
+ }
+
+ apr_pool_destroy(pchild);
+}
+
+
+
+void add_worker()
+{
+ int thread_slot;
+ int stacksize = ap_thread_stacksize == 0 ? 128*1024 : ap_thread_stacksize;
+
+ /* Find a free thread slot */
+ for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
+ if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
+ ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
+ ap_scoreboard_image->servers[child_slot][thread_slot].tid =
+ _beginthread(worker_main, NULL, stacksize, (void *)thread_slot);
+ break;
+ }
+ }
+}
+
+
+
+ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
+ EXCEPTIONREGISTRATIONRECORD *pRegRec,
+ CONTEXTRECORD *pContext,
+ PVOID p)
+{
+ int c;
+
+ if (pReportRec->fHandlerFlags & EH_NESTED_CALL) {
+ return XCPT_CONTINUE_SEARCH;
+ }
+
+ if (pReportRec->ExceptionNum == XCPT_ACCESS_VIOLATION ||
+ pReportRec->ExceptionNum == XCPT_INTEGER_DIVIDE_BY_ZERO) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00195)
+ "caught exception in worker thread, initiating child shutdown pid=%d", getpid());
+ for (c=0; c<HARD_THREAD_LIMIT; c++) {
+ if (ap_scoreboard_image->servers[child_slot][c].tid == _gettid()) {
+ ap_scoreboard_image->servers[child_slot][c].status = SERVER_DEAD;
+ break;
+ }
+ }
+
+ /* Shut down process ASAP, it could be quite unhealthy & leaking resources */
+ shutdown_pending = 1;
+ ap_scoreboard_image->parent[child_slot].quiescing = 1;
+ kill(getpid(), SIGHUP);
+ DosUnwindException(UNWIND_ALL, 0, 0);
+ }
+
+ return XCPT_CONTINUE_SEARCH;
+}
+
+
+
+static void worker_main(void *vpArg)
+{
+ apr_thread_t *thd = NULL;
+ apr_os_thread_t osthd;
+ long conn_id;
+ conn_rec *current_conn;
+ apr_pool_t *pconn;
+ apr_allocator_t *allocator;
+ apr_bucket_alloc_t *bucket_alloc;
+ worker_args_t *worker_args;
+ HQUEUE workq;
+ PID owner;
+ int rc;
+ REQUESTDATA rd;
+ ULONG len;
+ BYTE priority;
+ int thread_slot = (int)vpArg;
+ EXCEPTIONREGISTRATIONRECORD reg_rec = { NULL, thread_exception_handler };
+ ap_sb_handle_t *sbh;
+
+ /* Trap exceptions in this thread so we don't take down the whole process */
+ DosSetExceptionHandler( &reg_rec );
+
+ osthd = apr_os_thread_current();
+ apr_os_thread_put(&thd, &osthd, pchild);
+
+ rc = DosOpenQueue(&owner, &workq,
+ apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
+
+ if (rc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00196)
+ "unable to open work queue, exiting");
+ ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
+ }
+
+ conn_id = ID_FROM_CHILD_THREAD(child_slot, thread_slot);
+ ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_READY,
+ NULL);
+
+ apr_allocator_create(&allocator);
+ apr_allocator_max_free_set(allocator, ap_max_mem_free);
+ bucket_alloc = apr_bucket_alloc_create_ex(allocator);
+
+ while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
+ rc == 0 && rd.ulData != WORKTYPE_EXIT) {
+ pconn = worker_args->pconn;
+ ap_create_sb_handle(&sbh, pconn, child_slot, thread_slot);
+ current_conn = ap_run_create_connection(pconn, ap_server_conf,
+ worker_args->conn_sd, conn_id,
+ sbh, bucket_alloc);
+
+ if (current_conn) {
+ current_conn->current_thread = thd;
+ ap_process_connection(current_conn, worker_args->conn_sd);
+ ap_lingering_close(current_conn);
+ }
+
+ apr_pool_destroy(pconn);
+ ap_update_child_status_from_indexes(child_slot, thread_slot,
+ SERVER_READY, NULL);
+ }
+
+ ap_update_child_status_from_indexes(child_slot, thread_slot, SERVER_DEAD,
+ NULL);
+
+ apr_bucket_alloc_destroy(bucket_alloc);
+ apr_allocator_destroy(allocator);
+}
+
+
+
+static void server_maintenance(void *vpArg)
+{
+ int num_idle, num_needed;
+ ULONG num_pending = 0;
+ int threadnum;
+ HQUEUE workq;
+ ULONG rc;
+ PID owner;
+
+ rc = DosOpenQueue(&owner, &workq,
+ apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
+
+ if (rc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00197)
+ "unable to open work queue in maintenance thread");
+ return;
+ }
+
+ do {
+ for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
+ num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
+ }
+
+ DosQueryQueue(workq, &num_pending);
+ num_needed = ap_min_spare_threads - num_idle + num_pending;
+
+ if (num_needed > 0) {
+ for (threadnum=0; threadnum < num_needed; threadnum++) {
+ add_worker();
+ }
+ }
+
+ if (num_idle - num_pending > ap_max_spare_threads) {
+ DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
+ }
+ } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
+}
+
+
+
+/* Signal handling routines */
+
+static void sig_term(int sig)
+{
+ shutdown_pending = 1;
+ is_graceful = 0;
+ signal(SIGTERM, SIG_DFL);
+}
+
+
+
+static void sig_hup(int sig)
+{
+ shutdown_pending = 1;
+ is_graceful = 1;
+}
+
+
+
+static void set_signals()
+{
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sig_term;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00198) "sigaction(SIGTERM)");
+
+ sa.sa_handler = sig_hup;
+
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00199) "sigaction(SIGHUP)");
+}