diff options
Diffstat (limited to 'server/mpm/worker/worker.c')
-rw-r--r-- | server/mpm/worker/worker.c | 102 |
1 files changed, 95 insertions, 7 deletions
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 586999c2e3..2f54073f53 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -184,6 +184,7 @@ typedef struct { */ typedef struct { apr_thread_t **threads; + apr_thread_t *listener; int child_num_arg; apr_threadattr_t *threadattr; } thread_starter; @@ -229,6 +230,7 @@ static apr_pool_t *pchild; /* Pool for httpd child stuff */ static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main thread. Use this instead */ static pid_t parent_pid; +static apr_os_thread_t *listener_os_thread; /* Locks for accept serialization */ static apr_proc_mutex_t *accept_mutex; @@ -239,9 +241,33 @@ static apr_proc_mutex_t *accept_mutex; #define SAFE_ACCEPT(stmt) (stmt) #endif +/* The LISTENER_SIGNAL signal will be sent from the main thread to the + * listener thread to wake it up for graceful termination (what a child + * process from an old generation does when the admin does "apachectl + * graceful"). This signal will be blocked in all threads of a child + * process except for the listener thread. + */ +#define LISTENER_SIGNAL SIGHUP + +static void wakeup_listener(void) +{ + /* + * we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" and wake + * up the listener thread since it is the only thread with SIGHUP + * unblocked, but that doesn't work on Linux + */ + pthread_kill(*listener_os_thread, LISTENER_SIGNAL); +} + static void signal_workers(void) { workers_may_exit = 1; + + /* in case we weren't called from the listener thread, wake up the + * listener thread + */ + wakeup_listener(); + /* XXX: This will happen naturally on a graceful, and we don't care * otherwise. ap_queue_signal_all_wakeup(worker_queue); */ @@ -584,6 +610,13 @@ static void check_infinite_requests(void) } } +static void unblock_the_listener(int sig) +{ + /* XXX If specifying SIG_IGN is guaranteed to unblock a syscall, + * then we don't need this goofy function. + */ +} + static void *listener_thread(apr_thread_t *thd, void * dummy) { proc_info * ti = dummy; @@ -597,6 +630,8 @@ static void *listener_thread(apr_thread_t *thd, void * dummy) apr_pollfd_t *pollset; apr_status_t rv; ap_listen_rec *lr, *last_lr = ap_listeners; + struct sigaction sa; + sigset_t sig_mask; free(ti); @@ -604,6 +639,21 @@ static void *listener_thread(apr_thread_t *thd, void * dummy) for(lr = ap_listeners ; lr != NULL ; lr = lr->next) apr_poll_socket_add(pollset, lr->sd, APR_POLLIN); + sigemptyset(&sig_mask); + /* Unblock the signal used to wake this thread up, and set a handler for + * it. + */ + sigaddset(&sig_mask, LISTENER_SIGNAL); +#if defined(SIGPROCMASK_SETS_THREAD_MASK) + sigprocmask(SIG_UNBLOCK, &sig_mask, NULL); +#else + pthread_sigmask(SIG_UNBLOCK, &sig_mask, NULL); +#endif + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = unblock_the_listener; + sigaction(LISTENER_SIGNAL, &sa, NULL); + /* TODO: Switch to a system where threads reuse the results from earlier poll calls - manoj */ while (1) { @@ -617,6 +667,9 @@ static void *listener_thread(apr_thread_t *thd, void * dummy) != APR_SUCCESS) { int level = APLOG_EMERG; + if (workers_may_exit) { + break; + } if (ap_scoreboard_image->parent[process_slot].generation != ap_scoreboard_image->global->running_generation) { level = APLOG_DEBUG; /* common to get these at restart time */ @@ -685,8 +738,8 @@ static void *listener_thread(apr_thread_t *thd, void * dummy) rv = lr->accept_func(&csd, lr, ptrans); /* If we were interrupted for whatever reason, just start - * the main loop over again. (The worker MPM still uses - * signals in the one_process case.) */ + * the main loop over again. + */ if (APR_STATUS_IS_EINTR(rv)) { continue; } @@ -699,6 +752,9 @@ static void *listener_thread(apr_thread_t *thd, void * dummy) != APR_SUCCESS) { int level = APLOG_EMERG; + if (workers_may_exit) { + break; + } if (ap_scoreboard_image->parent[process_slot].generation != ap_scoreboard_image->global->running_generation) { level = APLOG_DEBUG; /* common to get these at restart time */ @@ -812,7 +868,6 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy) apr_status_t rv; int i = 0; int threads_created = 0; - apr_thread_t *listener; /* We must create the fd queues before we start up the listener * and worker threads. */ @@ -828,7 +883,7 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy) my_info->pid = my_child_num; my_info->tid = i; my_info->sd = 0; - rv = apr_thread_create(&listener, thread_attr, listener_thread, + 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, @@ -842,6 +897,7 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy) apr_sleep(10 * APR_USEC_PER_SEC); clean_child_exit(APEXIT_CHILDFATAL); } + apr_os_thread_get(&listener_os_thread, ts->listener); while (1) { /* ap_threads_per_child does not include the listener thread */ for (i = 0; i < ap_threads_per_child; i++) { @@ -901,11 +957,42 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy) return NULL; } -static void join_workers(apr_thread_t **threads) +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 + * workers_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 && pthread_kill(*listener_os_thread, 0) == 0) { + /* listener not dead yet */ + apr_sleep(APR_USEC_PER_SEC / 2); + wakeup_listener(); + ++iter; + } + if (iter >= 10) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 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]); @@ -1003,6 +1090,7 @@ static void child_main(int child_num_arg) apr_threadattr_detach_set(thread_attr, 0); ts->threads = threads; + ts->listener = NULL; ts->child_num_arg = child_num_arg; ts->threadattr = thread_attr; @@ -1038,7 +1126,7 @@ static void child_main(int child_num_arg) * If the worker hasn't exited, then this blocks until * they have (then cleans up). */ - join_workers(threads); + join_workers(ts->listener, threads); } else { /* !one_process */ /* Watch for any messages from the parent over the POD */ @@ -1062,7 +1150,7 @@ static void child_main(int child_num_arg) * If the worker hasn't exited, then this blocks until * they have (then cleans up). */ - join_workers(threads); + join_workers(ts->listener, threads); } } |