On Fri, 2009-10-30 at 14:59 +1100, Bojan Smojver wrote:
> Of course, SIGINT plucked out of thin air.
Actually, we can just reuse WORKER_SIGNAL for all this.
--
Bojan
--- httpd-2.2.14-v/server/mpm/worker/worker.c 2007-07-18 00:48:25.000000000 +1000
+++ httpd-2.2.14/server/mpm/worker/worker.c 2009-10-31 13:13:33.934697512 +1100
@@ -226,12 +226,118 @@
*/
#define WORKER_SIGNAL AP_SIG_GRACEFUL
+#ifdef HAVE_PTHREAD_KILL
+/* An array of thread and socket descriptors in use by each thread used to
+ * perform a non-graceful (forced) shutdown of the server. */
+static volatile sig_atomic_t suspend_workers;
+static struct worker_data {
+ apr_os_thread_t *thread;
+ apr_socket_t *socket;
+ volatile sig_atomic_t suspended;
+} *worker_data;
+#else
/* An array of socket descriptors in use by each thread used to
* perform a non-graceful (forced) shutdown of the server. */
static apr_socket_t **worker_sockets;
+#endif
+
+#ifdef HAVE_PTHREAD_KILL
+static void worker_signal_handler(int sig)
+{
+ int i;
+ sigset_t signal_set;
+
+ /* find our thread */
+ for (i = 0; i < ap_threads_per_child; i++) {
+ if (worker_data[i].thread &&
+ pthread_equal(*worker_data[i].thread, pthread_self())) {
+ break;
+ }
+ }
+
+ /* just in case we overshot */
+ if (i >= ap_threads_per_child) {
+ return;
+ }
+
+ /* if we are not being suspended, exit now */
+ if (!suspend_workers) {
+ worker_data[i].suspended = 0;
+ return;
+ }
+
+ sigfillset(&signal_set);
+ sigdelset(&signal_set, WORKER_SIGNAL);
+
+ worker_data[i].suspended = 1; /* safe - thread already executing here */
+
+ sigsuspend(&signal_set);
+
+ worker_data[i].suspended = 0;
+}
+
+static void interrupt_worker_threads()
+{
+ int i;
+
+ for (i = 0; i < ap_threads_per_child; i++) {
+ if (worker_data[i].thread) {
+ /* try sending the signal twice */
+ if (pthread_kill(*worker_data[i].thread, WORKER_SIGNAL) == -1) {
+ pthread_kill(*worker_data[i].thread, WORKER_SIGNAL);
+ }
+ }
+ }
+}
+
+static void suspend_worker_threads()
+{
+ suspend_workers = 1;
+ interrupt_worker_threads();
+}
+
+static void resume_worker_threads()
+{
+ suspend_workers = 0;
+ interrupt_worker_threads();
+}
static void close_worker_sockets(void)
{
+ int i, j, csd;
+
+ suspend_worker_threads();
+
+ /* wait for threads to suspend, but press ahead after a while anyway */
+ for (j = 0; j < 5; j++) {
+ int sum = 0;
+
+ apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
+
+ for (i = 0; i < ap_threads_per_child; i++) {
+ sum += worker_data[i].suspended;
+ }
+
+ if (sum == ap_threads_per_child) {
+ break;
+ }
+ }
+
+ /* shut down all client sockets */
+ for (i = 0; i < ap_threads_per_child; i++) {
+ if (worker_data[i].socket) {
+ apr_os_sock_get(&csd, worker_data[i].socket);
+ if (csd != -1) {
+ shutdown(csd, SHUT_RDWR);
+ }
+ }
+ }
+
+ resume_worker_threads();
+}
+#else
+static void close_worker_sockets(void)
+{
int i;
for (i = 0; i < ap_threads_per_child; i++) {
if (worker_sockets[i]) {
@@ -240,6 +346,7 @@
}
}
}
+#endif
static void wakeup_listener(void)
{
@@ -836,7 +943,7 @@
#ifdef HAVE_PTHREAD_KILL
unblock_signal(WORKER_SIGNAL);
- apr_signal(WORKER_SIGNAL, dummy_signal_handler);
+ apr_signal(WORKER_SIGNAL, worker_signal_handler);
#endif
while (!workers_may_exit) {
@@ -889,10 +996,22 @@
continue;
}
is_idle = 0;
+
+#ifdef HAVE_PTHREAD_KILL
+ worker_data[thread_slot].socket = csd;
+#else
worker_sockets[thread_slot] = csd;
+#endif
+
bucket_alloc = apr_bucket_alloc_create(ptrans);
process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc);
+
+#ifdef HAVE_PTHREAD_KILL
+ worker_data[thread_slot].socket = NULL;
+#else
worker_sockets[thread_slot] = NULL;
+#endif
+
requests_this_child--; /* FIXME: should be synchronized - aaron */
apr_pool_clear(ptrans);
last_ptrans = ptrans;
@@ -975,8 +1094,13 @@
clean_child_exit(APEXIT_CHILDFATAL);
}
+#ifdef HAVE_PTHREAD_KILL
+ worker_data = apr_pcalloc(pchild, ap_threads_per_child
+ * sizeof(*worker_data));
+#else
worker_sockets = apr_pcalloc(pchild, ap_threads_per_child
* sizeof(apr_socket_t *));
+#endif
loops = prev_threads_created = 0;
while (1) {
@@ -1012,6 +1136,9 @@
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
+#ifdef HAVE_PTHREAD_KILL
+ apr_os_thread_get(&worker_data[i].thread, threads[i]);
+#endif
threads_created++;
}
/* Start the listener only when there are workers available */