📄 mpm_winnt.c
字号:
/* AcceptEx on the completion context. The completion context will be * signaled when a connection is accepted. */ if (!AcceptEx(nlsd, pCompContext->accept_socket, pCompContext->buff, 0, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead, &pCompContext->Overlapped)) { lasterror = apr_get_netos_error(); if (lasterror == APR_FROM_OS_ERROR(WSAEINVAL)) { /* Hack alert. Occasionally, TransmitFile will not recycle the * accept socket (usually when the client disconnects early). * Get a new socket and try the call again. */ pCompContext->accept_socket = INVALID_SOCKET; ap_log_error(APLOG_MARK, APLOG_DEBUG, lasterror, server_conf, "winnt_accept: AcceptEx failed. Reallocate the accept socket and try again."); goto again; } else if (lasterror != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) { ap_log_error(APLOG_MARK,APLOG_ERR, lasterror, server_conf, "winnt_accept: AcceptEx failed. Process will exit."); // return -1; } /* Wait for pending i/o */ WaitForSingleObject(pCompContext->Overlapped.hEvent, INFINITE); } /* When a connection is received, send an io completion notification to * the ThreadDispatchIOCP */ PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED, &pCompContext->Overlapped); if (shutdown_in_progress) break; }}static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT pCompContext){ int requests_this_child = 0; int rc; DWORD BytesRead; DWORD CompKey; LPOVERLAPPED pol; /* Recycle the completion context. * - destroy the ptrans pool * - put the context on the queue to be consumed by the accept thread * Note: pCompContext->accept_socket may be in a disconnected * but reusable state so -don't- close it. */ if (pCompContext) { apr_pool_clear(pCompContext->ptrans); apr_pool_destroy(pCompContext->ptrans); pCompContext->ptrans = NULL; pCompContext->next = NULL; apr_lock_acquire(qlock); if (qtail) qtail->next = pCompContext; else qhead = pCompContext; qtail = pCompContext; apr_lock_release(qlock); } g_blocked_threads++; while (1) { if (workers_may_exit) { g_blocked_threads--; return NULL; } rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey, &pol, INFINITE); if (!rc) { rc = apr_get_os_error(); ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, server_conf, "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc); continue; } switch (CompKey) { case IOCP_CONNECTION_ACCEPTED: pCompContext = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped); break; case IOCP_SHUTDOWN: g_blocked_threads--; return NULL; default: g_blocked_threads--; return NULL; } break; } g_blocked_threads--; if ((rc = apr_pool_create(&pCompContext->ptrans, NULL)) != APR_SUCCESS) { ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, server_conf, "Child %d: apr_pool_create failed with rc %d", my_pid, rc); } /* Get the local & remote address */ GetAcceptExSockaddrs(pCompContext->buff, 0, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &pCompContext->sa_server, &pCompContext->sa_server_len, &pCompContext->sa_client, &pCompContext->sa_client_len); return pCompContext;}/* * worker_main() * Main entry point for the worker threads. Worker threads block in * win*_get_connection() awaiting a connection to service. */static void worker_main(int thread_num){ PCOMP_CONTEXT context = NULL; apr_os_sock_info_t sockinfo; while (1) { conn_rec *c; apr_int32_t disconnected; ap_update_child_status(0, thread_num, SERVER_READY, (request_rec *) NULL); /* Grab a connection off the network */ if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { context = win9x_get_connection(context); } else { context = winnt_get_connection(context); } if (!context) break; sockinfo.os_sock = &context->accept_socket; sockinfo.local = context->sa_server; sockinfo.remote = context->sa_client; sockinfo.family = APR_INET; sockinfo.type = SOCK_STREAM; apr_os_sock_make(&context->sock, &sockinfo, context->ptrans); c = ap_new_connection(context->ptrans, server_conf, context->sock, thread_num); if (c) { ap_process_connection(c); apr_getsocketopt(context->sock, APR_SO_DISCONNECTED, &disconnected); if (!disconnected) { context->accept_socket = INVALID_SOCKET; ap_lingering_close(c); } } else { /* ap_new_connection closes the socket on failure */ context->accept_socket = INVALID_SOCKET; } } ap_update_child_status(0, thread_num, SERVER_DEAD, (request_rec *) NULL); ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Thread exiting.", my_pid);}static void cleanup_thread(thread *handles, int *thread_cnt, int thread_to_clean){ int i; CloseHandle(handles[thread_to_clean]); for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++) handles[i] = handles[i + 1]; (*thread_cnt)--;}/* * child_main() * Entry point for the main control thread for the child process. * This thread creates the accept thread, worker threads and * monitors the child process for maintenance and shutdown * events. */static void child_main(){ apr_status_t status; HANDLE child_events[2]; char* exit_event_name; int nthreads = ap_threads_per_child; int tid; thread *child_handles; int rv; time_t end_time; int i; int cld; /* This is the child process or we are running in single process mode. */ exit_event_name = apr_psprintf(pconf, "apC%d", my_pid); setup_signal_names(apr_psprintf(pconf,"ap%d", parent_pid)); if (one_process) { /* Single process mode */ apr_lock_create(&start_mutex, APR_MUTEX, APR_CROSS_PROCESS, signal_name_prefix, pconf); exit_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name); } else { /* Child process mode */ apr_lock_child_init(&start_mutex, signal_name_prefix, pconf); exit_event = OpenEvent(EVENT_ALL_ACCESS, FALSE, exit_event_name); ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: exit_event_name = %s", my_pid, exit_event_name); } /* Initialize the child_events */ maintenance_event = CreateEvent(NULL, TRUE, FALSE, NULL); child_events[0] = exit_event; child_events[1] = maintenance_event; ap_assert(start_mutex); ap_assert(exit_event); ap_assert(maintenance_event); apr_pool_create(&pchild, pconf); allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL); apr_lock_create(&allowed_globals.jobmutex, APR_MUTEX, APR_INTRAPROCESS, NULL, pchild); /* * Wait until we have permission to start accepting connections. * start_mutex is used to ensure that only one child ever * goes into the listen/accept loop at once. */ status = apr_lock_acquire(start_mutex); if (status != APR_SUCCESS) { ap_log_error(APLOG_MARK,APLOG_ERR, status, server_conf, "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid); signal_parent(0); /* tell parent to die */ exit(0); } ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Acquired the start mutex.", my_pid); /* * Create the worker thread dispatch IOCompletionPort * on Windows NT/2000 */ if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) { /* Create the worker thread dispatch IOCP */ ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); /* CONCURRENT ACTIVE THREADS */ apr_lock_create(&qlock, APR_MUTEX, APR_INTRAPROCESS, NULL, pchild); } /* * Create the pool of worker threads */ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Starting %d worker threads.", my_pid, nthreads); child_handles = (thread) alloca(nthreads * sizeof(int)); for (i = 0; i < nthreads; i++) { ap_update_child_status(0, i, SERVER_STARTING, (request_rec *) NULL); child_handles[i] = (thread) _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) worker_main, (void *) i, 0, &tid); } /* * Start the accept thread */ if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) win9x_accept, (void *) i, 0, &tid); } else { /* Start an accept thread per listener */ SOCKET nlsd; /* native listening sock descriptor */ ap_listen_rec *lr; for (lr = ap_listeners; lr; lr = lr->next) { if (lr->sd != NULL) { apr_os_sock_get(&nlsd, lr->sd); _beginthreadex(NULL, 1000, (LPTHREAD_START_ROUTINE) winnt_accept, (void *) nlsd, 0, &tid); } } } /* Wait for one of three events: * exit_event: * The exit_event is signaled by the parent process to notify * the child that it is time to exit. * * maintenance_event: * This event is signaled by the worker thread pool to direct * this thread to create more completion contexts. * * TIMEOUT: * To do periodic maintenance on the server (check for thread exits, * number of completion contexts, etc.) */ while (1) { rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE); cld = rv - WAIT_OBJECT_0; if (rv == WAIT_FAILED) { /* Something serious is wrong */ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), server_conf, "Child %d: WAIT_FAILED -- shutting down server"); break; } else if (rv == WAIT_TIMEOUT) { /* Hey, this cannot happen */ ap_log_error(APLOG_MARK, APLOG_CRIT, APR_SUCCESS, server_conf, "Child %d: WAIT_TIMEOUT -- shutting down server", my_pid); break; } else if (cld == 0) { /* Exit event was signaled */ ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Exit event signaled. Child process is ending.", my_pid); break; } else { /* Child maintenance event signaled */ ResetEvent(maintenance_event); ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Child maintenance event signaled.", my_pid); } } /* Setting is_graceful will cause keep-alive connections to be closed * rather then block on the next network read. */ is_graceful = 1; /* Setting shutdown_in_progress prevents new connections from * being accepted but allows the worker threads to continue * handling connections that have already been accepted. */ shutdown_in_progress = 1; /* Close the listening sockets */ { ap_listen_rec *lr; for (lr = ap_listeners; lr ; lr = lr->next) { apr_socket_close(lr->sd); } } /* Give the worker threads time to handle already accepted connections */ Sleep(1000); /* Release the start_mutex to let the new process (in the restart * scenario) a chance to begin accepting and servicing requests */ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Releasing the start mutex", my_pid); apr_lock_release(start_mutex); /* Tell the worker threads they may exit when done handling
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -