📄 child.c
字号:
closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; } } else { /* ap_run_create_connection closes the socket on failure */ context->accept_socket = INVALID_SOCKET; } } ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, (request_rec *) NULL); ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, "Child %d: Worker thread %ld exiting.", my_pid, thread_num); return 0;}static void cleanup_thread(HANDLE *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 create_listener_thread(){ int tid; int num_listeners = 0; if (!use_acceptex) { _beginthreadex(NULL, 0, win9x_accept, NULL, 0, &tid); } else { /* Start an accept thread per listener * XXX: Why would we have a NULL sd in our listeners? */ ap_listen_rec *lr; /* Number of completion_contexts allowed in the system is * (ap_threads_per_child + num_listeners). We need the additional * completion contexts to prevent server hangs when ThreadsPerChild * is configured to something less than or equal to the number * of listeners. This is not a usual case, but people have * encountered it. * */ for (lr = ap_listeners; lr ; lr = lr->next) { num_listeners++; } max_num_completion_contexts = ap_threads_per_child + num_listeners; /* Now start a thread per listener */ for (lr = ap_listeners; lr; lr = lr->next) { if (lr->sd != NULL) { _beginthreadex(NULL, 1000, winnt_accept, (void *) lr, 0, &tid); } } }}void child_main(apr_pool_t *pconf){ apr_status_t status; apr_hash_t *ht; ap_listen_rec *lr; HANDLE child_events[2]; int threads_created = 0; int listener_started = 0; int tid; HANDLE *child_handles; int rv; time_t end_time; int i; int cld; apr_pool_create(&pchild, pconf); apr_pool_tag(pchild, "pchild"); ap_run_child_init(pchild, ap_server_conf); ht = apr_hash_make(pchild); /* Initialize the child_events */ max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (!max_requests_per_child_event) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Child %d: Failed to create a max_requests event.", my_pid); exit(APEXIT_CHILDINIT); } child_events[0] = exit_event; child_events[1] = max_requests_per_child_event; allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL); apr_thread_mutex_create(&allowed_globals.jobmutex, APR_THREAD_MUTEX_DEFAULT, 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_proc_mutex_lock(start_mutex); if (status != APR_SUCCESS) { ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf, "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid); exit(APEXIT_CHILDINIT); } ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: Acquired the start mutex.", my_pid); /* * Create the worker thread dispatch IOCompletionPort * on Windows NT/2000 */ if (use_acceptex) { /* Create the worker thread dispatch IOCP */ ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); /* CONCURRENT ACTIVE THREADS */ apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild); qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (!qwait_event) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Child %d: Failed to create a qwait event.", my_pid); exit(APEXIT_CHILDINIT); } } /* * Create the pool of worker threads */ ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: Starting %d worker threads.", my_pid, ap_threads_per_child); child_handles = (HANDLE) apr_pcalloc(pchild, ap_threads_per_child * sizeof(int)); apr_thread_mutex_create(&child_lock, APR_THREAD_MUTEX_DEFAULT, pchild); while (1) { for (i = 0; i < ap_threads_per_child; i++) { int *score_idx; int status = ap_scoreboard_image->servers[0][i].status; if (status != SERVER_GRACEFUL && status != SERVER_DEAD) { continue; } ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL); child_handles[i] = (HANDLE) _beginthreadex(NULL, 0, worker_main, (void *) i, 0, &tid); if (child_handles[i] == 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Child %d: _beginthreadex failed. Unable to create all worker threads. " "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.", my_pid, threads_created, ap_threads_per_child); ap_signal_parent(SIGNAL_PARENT_SHUTDOWN); goto shutdown; } threads_created++; /* Save the score board index in ht keyed to the thread handle. We need this * when cleaning up threads down below... */ apr_thread_mutex_lock(child_lock); score_idx = apr_pcalloc(pchild, sizeof(int)); *score_idx = i; apr_hash_set(ht, &child_handles[i], sizeof(HANDLE), score_idx); apr_thread_mutex_unlock(child_lock); } /* Start the listener only when workers are available */ if (!listener_started && threads_created) { create_listener_thread(); listener_started = 1; winnt_mpm_state = AP_MPMQ_RUNNING; } if (threads_created == ap_threads_per_child) { break; } /* Check to see if the child has been told to exit */ if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) { break; } /* wait for previous generation to clean up an entry in the scoreboard */ apr_sleep(1 * APR_USEC_PER_SEC); } /* 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. * * max_requests_per_child_event: * This event is signaled by the worker threads to indicate that * the process has handled MaxRequestsPerChild connections. * * TIMEOUT: * To do periodic maintenance on the server (check for thread exits, * number of completion contexts, etc.) * * XXX: thread exits *aren't* being checked. * * XXX: other_child - we need the process handles to the other children * in order to map them to apr_proc_other_child_read (which is not * named well, it's more like a_p_o_c_died.) * * XXX: however - if we get a_p_o_c handle inheritance working, and * the parent process creates other children and passes the pipes * to our worker processes, then we have no business doing such * things in the child_main loop, but should happen in master_main. */ while (1) {#if !APR_HAS_OTHER_CHILD rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE); cld = rv - WAIT_OBJECT_0;#else rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000); cld = rv - WAIT_OBJECT_0; if (rv == WAIT_TIMEOUT) { apr_proc_other_child_check(); } else #endif if (rv == WAIT_FAILED) { /* Something serious is wrong */ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Child %d: WAIT_FAILED -- shutting down server", my_pid); break; } else if (cld == 0) { /* Exit event was signaled */ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: Exit event signaled. Child process is ending.", my_pid); break; } else { /* MaxRequestsPerChild event set by the worker threads. * Signal the parent to restart */ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: Process exiting because it reached " "MaxRequestsPerChild. Signaling the parent to " "restart a new child process.", my_pid); ap_signal_parent(SIGNAL_PARENT_RESTART); break; } } /* * Time to shutdown the child process */ shutdown: winnt_mpm_state = AP_MPMQ_STOPPING; /* Setting is_graceful will cause threads handling keep-alive connections * to close the connection after handling the current request. */ is_graceful = 1; /* Close the listening sockets. Note, we must close the listeners * before closing any accept sockets pending in AcceptEx to prevent * memory leaks in the kernel. */ for (lr = ap_listeners; lr ; lr = lr->next) { apr_socket_close(lr->sd); } /* Shutdown listener threads and pending AcceptEx socksts * but allow the worker threads to continue consuming from * the queue of accepted connections. */ shutdown_in_progress = 1; Sleep(1000); /* Tell the worker threads to exit */ workers_may_exit = 1; /* Release the start_mutex to let the new process (in the restart * scenario) a chance to begin accepting and servicing requests */ rv = apr_proc_mutex_unlock(start_mutex); if (rv == APR_SUCCESS) { ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf, "Child %d: Released the start mutex", my_pid); } else { ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, "Child %d: Failure releasing the start mutex", my_pid); } /* Shutdown the worker threads */ if (!use_acceptex) { for (i = 0; i < threads_created; i++) { add_job(INVALID_SOCKET); } } else { /* Windows NT/2000 */ /* Post worker threads blocked on the ThreadDispatch IOCompletion port */ while (g_blocked_threads > 0) { ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf, "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads); for (i=g_blocked_threads; i > 0; i--) { PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL); } Sleep(1000); } /* Empty the accept queue of completion contexts */ apr_thread_mutex_lock(qlock); while (qhead) { CloseHandle(qhead->Overlapped.hEvent); closesocket(qhead->accept_socket); qhead = qhead->next; } apr_thread_mutex_unlock(qlock); } /* Give busy worker threads a chance to service their connections */ ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: Waiting for %d worker threads to exit.", my_pid, threads_created); end_time = time(NULL) + 180; while (threads_created) { rv = wait_for_many_objects(threads_created, child_handles, end_time - time(NULL)); if (rv != WAIT_TIMEOUT) { rv = rv - WAIT_OBJECT_0; ap_assert((rv >= 0) && (rv < threads_created)); cleanup_thread(child_handles, &threads_created, rv); continue; } break; } /* Kill remaining threads off the hard way */ if (threads_created) { ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: Terminating %d threads that failed to exit.", my_pid, threads_created); } for (i = 0; i < threads_created; i++) { int *score_idx; TerminateThread(child_handles[i], 1); CloseHandle(child_handles[i]); /* Reset the scoreboard entry for the thread we just whacked */ score_idx = apr_hash_get(ht, &child_handles[i], sizeof(HANDLE)); ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL); } ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Child %d: All worker threads have exited.", my_pid); CloseHandle(allowed_globals.jobsemaphore); apr_thread_mutex_destroy(allowed_globals.jobmutex); apr_thread_mutex_destroy(child_lock); if (use_acceptex) { apr_thread_mutex_destroy(qlock); CloseHandle(qwait_event); } apr_pool_destroy(pchild); CloseHandle(exit_event);}#endif /* def WIN32 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -