📄 child.c
字号:
/* 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. */#ifdef WIN32#define CORE_PRIVATE #include "httpd.h" #include "http_main.h" #include "http_log.h" #include "http_config.h" /* for read_config */ #include "http_core.h" /* for get_remote_host */ #include "http_connection.h"#include "apr_portable.h"#include "apr_thread_proc.h"#include "apr_getopt.h"#include "apr_strings.h"#include "apr_lib.h"#include "apr_shm.h"#include "apr_thread_mutex.h"#include "ap_mpm.h"#include "ap_config.h"#include "ap_listen.h"#include "mpm_default.h"#include "mpm_winnt.h"#include "mpm_common.h"#include <malloc.h>#include "apr_atomic.h"/* shared with mpm_winnt.c */extern DWORD my_pid;/* used by parent to signal the child to start and exit *//* shared with mpm_winnt.c, but should be private to child.c */apr_proc_mutex_t *start_mutex;HANDLE exit_event;/* child_main() should never need to modify is_graceful!?! */extern int volatile is_graceful;/* Queue for managing the passing of COMP_CONTEXTs between * the accept and worker threads. */static apr_pool_t *pchild;static int shutdown_in_progress = 0;static int workers_may_exit = 0;static unsigned int g_blocked_threads = 0;static HANDLE max_requests_per_child_event;static apr_thread_mutex_t *child_lock;static apr_thread_mutex_t *qlock;static PCOMP_CONTEXT qhead = NULL;static PCOMP_CONTEXT qtail = NULL;static int num_completion_contexts = 0;static int max_num_completion_contexts = 0;static HANDLE ThreadDispatchIOCP = NULL;static HANDLE qwait_event = NULL;AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context){ /* Recycle the completion context. * - clear the ptrans pool * - put the context on the queue to be consumed by the accept thread * Note: * context->accept_socket may be in a disconnected but reusable * state so -don't- close it. */ if (context) { apr_pool_clear(context->ptrans); context->ba = apr_bucket_alloc_create(context->ptrans); context->next = NULL; ResetEvent(context->Overlapped.hEvent); apr_thread_mutex_lock(qlock); if (qtail) { qtail->next = context; } else { qhead = context; SetEvent(qwait_event); } qtail = context; apr_thread_mutex_unlock(qlock); }}AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void){ apr_status_t rv; PCOMP_CONTEXT context = NULL; while (1) { /* Grab a context off the queue */ apr_thread_mutex_lock(qlock); if (qhead) { context = qhead; qhead = qhead->next; if (!qhead) qtail = NULL; } else { ResetEvent(qwait_event); } apr_thread_mutex_unlock(qlock); if (!context) { /* We failed to grab a context off the queue, consider allocating * a new one out of the child pool. There may be up to * (ap_threads_per_child + num_listeners) contexts in the system * at once. */ if (num_completion_contexts >= max_num_completion_contexts) { /* All workers are busy, need to wait for one */ static int reported = 0; if (!reported) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, "Server ran out of threads to serve requests. Consider " "raising the ThreadsPerChild setting"); reported = 1; } /* Wait for a worker to free a context. Once per second, give * the caller a chance to check for shutdown. If the wait * succeeds, get the context off the queue. It must be available, * since there's only one consumer. */ rv = WaitForSingleObject(qwait_event, 1000); if (rv == WAIT_OBJECT_0) continue; else /* Hopefully, WAIT_TIMEOUT */ return NULL; } else { /* Allocate another context. * Note: * Multiple failures in the next two steps will cause the pchild pool * to 'leak' storage. I don't think this is worth fixing... */ apr_allocator_t *allocator; apr_thread_mutex_lock(child_lock); context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT)); context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (context->Overlapped.hEvent == NULL) { /* Hopefully this is a temporary condition ... */ ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf, "mpm_get_completion_context: CreateEvent failed."); apr_thread_mutex_unlock(child_lock); return NULL; } /* Create the tranaction pool */ apr_allocator_create(&allocator); apr_allocator_max_free_set(allocator, ap_max_mem_free); rv = apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf, "mpm_get_completion_context: Failed to create the transaction pool."); CloseHandle(context->Overlapped.hEvent); apr_thread_mutex_unlock(child_lock); return NULL; } apr_allocator_owner_set(allocator, context->ptrans); apr_pool_tag(context->ptrans, "transaction"); context->accept_socket = INVALID_SOCKET; context->ba = apr_bucket_alloc_create(context->ptrans); apr_atomic_inc(&num_completion_contexts); apr_thread_mutex_unlock(child_lock); break; } } else { /* Got a context from the queue */ break; } } return context;}AP_DECLARE(apr_status_t) mpm_post_completion_context(PCOMP_CONTEXT context, io_state_e state){ LPOVERLAPPED pOverlapped; if (context) pOverlapped = &context->Overlapped; else pOverlapped = NULL; PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped); return APR_SUCCESS;}/* * find_ready_listener() * Only used by Win9* and should go away when the win9*_accept() function is * reimplemented using apr_poll(). */static ap_listen_rec *head_listener;static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds){ ap_listen_rec *lr; SOCKET nsd; lr = head_listener; do { apr_os_sock_get(&nsd, lr->sd); if (FD_ISSET(nsd, main_fds)) { head_listener = lr->next; if (!head_listener) { head_listener = ap_listeners; } return lr; } lr = lr->next; if (!lr) { lr = ap_listeners; } } while (lr != head_listener); return NULL;}/* Windows 9x specific code... * Accept processing for on Windows 95/98 uses a producer/consumer queue * model. A single thread accepts connections and queues the accepted socket * to the accept queue for consumption by a pool of worker threads. * * win9x_accept() * The accept threads runs this function, which accepts connections off * the network and calls add_job() to queue jobs to the accept_queue. * add_job()/remove_job() * Add or remove an accepted socket from the list of sockets * connected to clients. allowed_globals.jobmutex protects * against multiple concurrent access to the linked list of jobs. * win9x_get_connection() * Calls remove_job() to pull a job from the accept queue. All the worker * threads block on remove_job. */typedef struct joblist_s { struct joblist_s *next; int sock;} joblist;typedef struct globals_s { HANDLE jobsemaphore; joblist *jobhead; joblist *jobtail; apr_thread_mutex_t *jobmutex; int jobcount;} globals;globals allowed_globals = {NULL, NULL, NULL, NULL, 0};#define MAX_SELECT_ERRORS 100static void add_job(int sock){ joblist *new_job; new_job = (joblist *) malloc(sizeof(joblist)); if (new_job == NULL) { ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Ouch! Out of memory in add_job()!"); return; } new_job->next = NULL; new_job->sock = sock; apr_thread_mutex_lock(allowed_globals.jobmutex); if (allowed_globals.jobtail != NULL) allowed_globals.jobtail->next = new_job; allowed_globals.jobtail = new_job; if (!allowed_globals.jobhead) allowed_globals.jobhead = new_job; allowed_globals.jobcount++; ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL); apr_thread_mutex_unlock(allowed_globals.jobmutex);}static int remove_job(void){ joblist *job; int sock; WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE); apr_thread_mutex_lock(allowed_globals.jobmutex); if (shutdown_in_progress && !allowed_globals.jobhead) { apr_thread_mutex_unlock(allowed_globals.jobmutex); return (INVALID_SOCKET); } job = allowed_globals.jobhead; ap_assert(job); allowed_globals.jobhead = job->next; if (allowed_globals.jobhead == NULL) allowed_globals.jobtail = NULL; apr_thread_mutex_unlock(allowed_globals.jobmutex); sock = job->sock; free(job); return (sock);}static unsigned int __stdcall win9x_accept(void * dummy){ struct timeval tv; fd_set main_fds; int wait_time = 1; int csd; SOCKET nsd = INVALID_SOCKET; struct sockaddr_in sa_client; int count_select_errors = 0; int rc; int clen; ap_listen_rec *lr; struct fd_set listenfds; SOCKET listenmaxfd = INVALID_SOCKET; /* Setup the listeners * ToDo: Use apr_poll() */ FD_ZERO(&listenfds); for (lr = ap_listeners; lr; lr = lr->next) { if (lr->sd != NULL) { apr_os_sock_get(&nsd, lr->sd); FD_SET(nsd, &listenfds); if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) { listenmaxfd = nsd; } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "Child %d: Listening on port %d.", my_pid, lr->bind_addr->port); } } head_listener = ap_listeners; while (!shutdown_in_progress) { tv.tv_sec = wait_time; tv.tv_usec = 0; memcpy(&main_fds, &listenfds, sizeof(fd_set)); rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) { count_select_errors = 0; /* reset count of errors */ continue; } else if (rc == SOCKET_ERROR) { /* A "real" error occurred, log it and increment the count of * select errors. This count is used to ensure we don't go into * a busy loop of continuous errors. */ ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf, "select failed with error %d", apr_get_netos_error()); count_select_errors++; if (count_select_errors > MAX_SELECT_ERRORS) { shutdown_in_progress = 1; ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf, "Too many errors in select loop. Child process exiting."); break; } } else { ap_listen_rec *lr; lr = find_ready_listener(&main_fds); if (lr != NULL) { /* fetch the native socket descriptor */ apr_os_sock_get(&nsd, lr->sd); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -