📄 worker.c
字号:
} else { if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex))) != APR_SUCCESS) { int level = APLOG_EMERG; if (ap_scoreboard_image->parent[process_slot].generation != ap_scoreboard_image->global->running_generation) { level = APLOG_DEBUG; /* common to get these at restart time */ } ap_log_error(APLOG_MARK, level, rv, ap_server_conf, "apr_proc_mutex_unlock failed. Attempting to " "shutdown process gracefully."); signal_threads(ST_GRACEFUL); } break; } } ap_queue_term(worker_queue); dying = 1; ap_scoreboard_image->parent[process_slot].quiescing = 1; /* wake up the main thread */ kill(ap_my_pid, SIGTERM); apr_thread_exit(thd, APR_SUCCESS); return NULL;}/* XXX For ungraceful termination/restart, we definitely don't want to * wait for active connections to finish but we may want to wait * for idle workers to get out of the queue code and release mutexes, * since those mutexes are cleaned up pretty soon and some systems * may not react favorably (i.e., segfault) if operations are attempted * on cleaned-up mutexes. */static void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy){ proc_info * ti = dummy; int process_slot = ti->pid; int thread_slot = ti->tid; apr_socket_t *csd = NULL; apr_bucket_alloc_t *bucket_alloc; apr_pool_t *last_ptrans = NULL; apr_pool_t *ptrans; /* Pool for per-transaction stuff */ apr_status_t rv; int is_idle = 0; free(ti); ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL); while (!workers_may_exit) { if (!is_idle) { rv = ap_queue_info_set_idle(worker_queue_info, last_ptrans); last_ptrans = NULL; if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, "ap_queue_info_set_idle failed. Attempting to " "shutdown process gracefully."); signal_threads(ST_GRACEFUL); break; } is_idle = 1; } ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_READY, NULL);worker_pop: if (workers_may_exit) { break; } rv = ap_queue_pop(worker_queue, &csd, &ptrans); if (rv != APR_SUCCESS) { /* We get APR_EOF during a graceful shutdown once all the connections * accepted by this server process have been handled. */ if (APR_STATUS_IS_EOF(rv)) { break; } /* We get APR_EINTR whenever ap_queue_pop() has been interrupted * from an explicit call to ap_queue_interrupt_all(). This allows * us to unblock threads stuck in ap_queue_pop() when a shutdown * is pending. * * If workers_may_exit is set and this is ungraceful termination/ * restart, we are bound to get an error on some systems (e.g., * AIX, which sanity-checks mutex operations) since the queue * may have already been cleaned up. Don't log the "error" if * workers_may_exit is set. */ else if (APR_STATUS_IS_EINTR(rv)) { goto worker_pop; } /* We got some other error. */ else if (!workers_may_exit) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "ap_queue_pop failed"); } continue; } is_idle = 0; worker_sockets[thread_slot] = csd; bucket_alloc = apr_bucket_alloc_create(ptrans); process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc); worker_sockets[thread_slot] = NULL; requests_this_child--; /* FIXME: should be synchronized - aaron */ apr_pool_clear(ptrans); last_ptrans = ptrans; } ap_update_child_status_from_indexes(process_slot, thread_slot, (dying) ? SERVER_DEAD : SERVER_GRACEFUL, (request_rec *) NULL); apr_thread_exit(thd, APR_SUCCESS); return NULL;}static int check_signal(int signum){ switch (signum) { case SIGTERM: case SIGINT: return 1; } return 0;}static void create_listener_thread(thread_starter *ts){ int my_child_num = ts->child_num_arg; apr_threadattr_t *thread_attr = ts->threadattr; proc_info *my_info; apr_status_t rv; my_info = (proc_info *)malloc(sizeof(proc_info)); my_info->pid = my_child_num; my_info->tid = -1; /* listener thread doesn't have a thread slot */ my_info->sd = 0; rv = apr_thread_create(&ts->listener, thread_attr, listener_thread, my_info, pchild); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, "apr_thread_create: unable to create listener thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } apr_os_thread_get(&listener_os_thread, ts->listener);}/* XXX under some circumstances not understood, children can get stuck * in start_threads forever trying to take over slots which will * never be cleaned up; for now there is an APLOG_DEBUG message issued * every so often when this condition occurs */static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy){ thread_starter *ts = dummy; apr_thread_t **threads = ts->threads; apr_threadattr_t *thread_attr = ts->threadattr; int child_num_arg = ts->child_num_arg; int my_child_num = child_num_arg; proc_info *my_info; apr_status_t rv; int i; int threads_created = 0; int listener_started = 0; int loops; int prev_threads_created; /* We must create the fd queues before we start up the listener * and worker threads. */ worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue)); rv = ap_queue_init(worker_queue, ap_threads_per_child, pchild); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, "ap_queue_init() failed"); clean_child_exit(APEXIT_CHILDFATAL); } rv = ap_queue_info_create(&worker_queue_info, pchild, ap_threads_per_child); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, "ap_queue_info_create() failed"); clean_child_exit(APEXIT_CHILDFATAL); } worker_sockets = apr_pcalloc(pchild, ap_threads_per_child * sizeof(apr_socket_t *)); loops = prev_threads_created = 0; while (1) { /* ap_threads_per_child does not include the listener thread */ for (i = 0; i < ap_threads_per_child; i++) { int status = ap_scoreboard_image->servers[child_num_arg][i].status; if (status != SERVER_GRACEFUL && status != SERVER_DEAD) { continue; } my_info = (proc_info *)malloc(sizeof(proc_info)); if (my_info == NULL) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf, "malloc: out of memory"); clean_child_exit(APEXIT_CHILDFATAL); } my_info->pid = my_child_num; my_info->tid = i; my_info->sd = 0; /* We are creating threads right now */ ap_update_child_status_from_indexes(my_child_num, i, SERVER_STARTING, NULL); /* We let each thread update its own scoreboard entry. This is * done because it lets us deal with tid better. */ rv = apr_thread_create(&threads[i], thread_attr, worker_thread, my_info, pchild); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, "apr_thread_create: unable to create worker thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } threads_created++; } /* Start the listener only when there are workers available */ if (!listener_started && threads_created) { create_listener_thread(ts); listener_started = 1; } if (start_thread_may_exit || threads_created == ap_threads_per_child) { break; } /* wait for previous generation to clean up an entry */ apr_sleep(apr_time_from_sec(1)); ++loops; if (loops % 120 == 0) { /* every couple of minutes */ if (prev_threads_created == threads_created) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "child %" APR_PID_T_FMT " isn't taking over " "slots very quickly (%d of %d)", ap_my_pid, threads_created, ap_threads_per_child); } prev_threads_created = threads_created; } } /* What state should this child_main process be listed as in the * scoreboard...? * ap_update_child_status_from_indexes(my_child_num, i, SERVER_STARTING, * (request_rec *) NULL); * * This state should be listed separately in the scoreboard, in some kind * of process_status, not mixed in with the worker threads' status. * "life_status" is almost right, but it's in the worker's structure, and * the name could be clearer. gla */ apr_thread_exit(thd, APR_SUCCESS); return NULL;}static void join_workers(apr_thread_t *listener, apr_thread_t **threads){ int i; apr_status_t rv, thread_rv; if (listener) { int iter; /* deal with a rare timing window which affects waking up the * listener thread... if the signal sent to the listener thread * is delivered between the time it verifies that the * listener_may_exit flag is clear and the time it enters a * blocking syscall, the signal didn't do any good... work around * that by sleeping briefly and sending it again */ iter = 0; while (iter < 10 && #ifdef HAVE_PTHREAD_KILL pthread_kill(*listener_os_thread, 0)#else kill(ap_my_pid, 0)#endif == 0) { /* listener not dead yet */ apr_sleep(apr_time_make(0, 500000)); wakeup_listener(); ++iter; } if (iter >= 10) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "the listener thread didn't exit"); } else { rv = apr_thread_join(&thread_rv, listener); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "apr_thread_join: unable to join listener thread"); } } } for (i = 0; i < ap_threads_per_child; i++) { if (threads[i]) { /* if we ever created this thread */ rv = apr_thread_join(&thread_rv, threads[i]); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "apr_thread_join: unable to join worker " "thread %d", i); } } }}static void join_start_thread(apr_thread_t *start_thread_id){ apr_status_t rv, thread_rv; start_thread_may_exit = 1; /* tell it to give up in case it is still * trying to take over slots from a * previous generation */ rv = apr_thread_join(&thread_rv, start_thread_id); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "apr_thread_join: unable to join the start " "thread"); }}static void child_main(int child_num_arg){ apr_thread_t **threads; apr_status_t rv; thread_starter *ts; apr_threadattr_t *thread_attr; apr_thread_t *start_thread_id; mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this * child initializes */ ap_my_pid = getpid(); ap_fatal_signal_child_setup(ap_server_conf); apr_pool_create(&pchild, pconf); /*stuff to do before we switch id's, so we have permissions.*/ ap_reopen_scoreboard(pchild, NULL, 0); rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname, pchild)); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, "Couldn't initialize cross-process lock in child"); clean_child_exit(APEXIT_CHILDFATAL); } if (unixd_setup_child()) { clean_child_exit(APEXIT_CHILDFATAL); } ap_run_child_init(pchild, ap_server_conf); /* done with init critical section */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -