changeset 9236:a878a0897eb9 HEAD

lib-signals: Redesigned how delayed signals are handled. Fixes signal handler hanging infinitely in write() when many signals were sent rapidly.
author Timo Sirainen <tss@iki.fi>
date Tue, 21 Jul 2009 17:14:41 -0400
parents 8e66ca02b6f9
children 72f159e0aaa6
files src/lib/lib-signals.c
diffstat 1 files changed, 43 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/lib-signals.c	Tue Jul 21 15:18:49 2009 -0400
+++ b/src/lib/lib-signals.c	Tue Jul 21 17:14:41 2009 -0400
@@ -27,6 +27,9 @@
 static bool signals_initialized = FALSE;
 static struct io *io_sig = NULL;
 
+static siginfo_t pending_signals[MAX_SIGNAL_VALUE+1];
+static bool have_pending_signals = FALSE;
+
 const char *lib_signal_code_to_str(int signo, int sicode)
 {
 	/* common */
@@ -70,7 +73,7 @@
 static void sig_handler(int signo, siginfo_t *si, void *context ATTR_UNUSED)
 {
 	struct signal_handler *h;
-	bool delayed_sent = FALSE;
+	char c = 0;
 
 	if (signo < 0 || signo > MAX_SIGNAL_VALUE)
 		return;
@@ -80,14 +83,16 @@
 	for (h = signal_handlers[signo]; h != NULL; h = h->next) {
 		if (!h->delayed)
 			h->handler(si, h->context);
-		else if (!delayed_sent) {
-			int saved_errno = errno;
+		else if (pending_signals[signo].si_signo == 0) {
+			pending_signals[signo] = *si;
+			if (!have_pending_signals) {
+				int saved_errno = errno;
 
-			if (write(sig_pipe_fd[1], si,
-				  sizeof(*si)) != sizeof(*si))
-				i_error("write(sigpipe) failed: %m");
-			delayed_sent = TRUE;
-			errno = saved_errno;
+				if (write(sig_pipe_fd[1], &c, 1) != 1)
+					i_error("write(sigpipe) failed: %m");
+				have_pending_signals = TRUE;
+				errno = saved_errno;
+			}
 		}
 	}
 }
@@ -101,48 +106,46 @@
 
 static void signal_read(void *context ATTR_UNUSED)
 {
-	siginfo_t signal_buf[10];
 	siginfo_t signals[MAX_SIGNAL_VALUE+1];
-	ssize_t i, ret;
+	sigset_t fullset, oldset;
+	struct signal_handler *h;
+	char buf[2];
 	int signo;
-
-	ret = read(sig_pipe_fd[0], signal_buf, sizeof(signal_buf));
-	if (ret > 0) {
-		if (ret % sizeof(siginfo_t) != 0)
-			i_fatal("read(sigpipe) returned partial data");
-		ret /= sizeof(siginfo_t);
+	ssize_t ret;
 
-		/* get rid of duplicate signals */
-		memset(signals, 0, sizeof(signals));
-		for (i = 0; i < ret; i++) {
-			signo = signal_buf[i].si_signo;
-			if (signo > MAX_SIGNAL_VALUE) {
-				i_panic("sigpipe contains signal %d > %d",
-					signo, MAX_SIGNAL_VALUE);
-			}
-			signals[signo] = signal_buf[i];
-		}
+	if (sigfillset(&fullset) < 0)
+		i_fatal("sigfillset() failed: %m");
+	if (sigprocmask(SIG_BLOCK, &fullset, &oldset) < 0)
+		i_fatal("sigprocmask() failed: %m");
 
-		/* call the delayed handlers */
-		for (signo = 0; signo < MAX_SIGNAL_VALUE; signo++) {
-			if (signals[signo].si_signo > 0) {
-				struct signal_handler *h =
-					signal_handlers[signo];
-
-				for (; h != NULL; h = h->next) {
-					if (h->delayed) {
-						h->handler(&signals[signo],
-							   h->context);
-					}
-				}
-			}
-		}
+	ret = read(sig_pipe_fd[0], buf, sizeof(buf));
+	i_assert(ret <= 1);
+	if (ret > 0) {
+		memcpy(signals, pending_signals, sizeof(signals));
+		memset(pending_signals, 0, sizeof(pending_signals));
+		have_pending_signals = FALSE;
 	} else if (ret < 0) {
 		if (errno != EAGAIN)
 			i_fatal("read(sigpipe) failed: %m");
 	} else {
 		i_fatal("read(sigpipe) failed: EOF");
 	}
+	if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
+		i_fatal("sigprocmask() failed: %m");
+
+	if (ret < 0)
+		return;
+
+	/* call the delayed handlers after signals are copied and unblocked */
+	for (signo = 0; signo < MAX_SIGNAL_VALUE; signo++) {
+		if (signals[signo].si_signo == 0)
+			continue;
+
+		for (h = signal_handlers[signo]; h != NULL; h = h->next) {
+			if (h->delayed)
+				h->handler(&signals[signo], h->context);
+		}
+	}
 }
 
 static void lib_signals_set(int signo, bool ignore)