diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 92f2077..165c4aa 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -126,6 +126,7 @@
 #include "miscadmin.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
+#include "storage/proc.h"
 #include "storage/procarray.h"
 #include "storage/procsignal.h"
 #include "storage/sinval.h"
@@ -341,10 +342,9 @@ static List *upperPendingNotifies = NIL;		/* list of upper-xact lists */
  *
  * NB: the "volatile" on these declarations is critical!  If your compiler
  * does not grok "volatile", you'd be best advised to compile this file
- * with all optimization turned off.
- */
-static volatile sig_atomic_t notifyInterruptEnabled = 0;
-static volatile sig_atomic_t notifyInterruptOccurred = 0;
+ * with all optimization turned off. */
+volatile sig_atomic_t notifyInterruptOK = 0;
+volatile sig_atomic_t notifyInterruptPending = 0;
 
 /* True if we've registered an on_shmem_exit cleanup */
 static bool unlistenExitRegistered = false;
@@ -1625,11 +1625,8 @@ AtSubAbort_Notify(void)
 /*
  * HandleNotifyInterrupt
  *
- *		This is called when PROCSIG_NOTIFY_INTERRUPT is received.
- *
- *		If we are idle (notifyInterruptEnabled is set), we can safely invoke
- *		ProcessIncomingNotify directly.  Otherwise, just set a flag
- *		to do it later.
+ *		This is called the next time ProcessInterrupts is run after
+ *		PROCSIG_NOTIFY_INTERRUPT has been received.
  */
 void
 HandleNotifyInterrupt(void)
@@ -1641,69 +1638,13 @@ HandleNotifyInterrupt(void)
 	 * they were ever turned on.
 	 */
 
-	/* Don't joggle the elbow of proc_exit */
-	if (proc_exit_inprogress)
-		return;
-
-	if (notifyInterruptEnabled)
-	{
-		bool		save_ImmediateInterruptOK = ImmediateInterruptOK;
-
-		/*
-		 * We may be called while ImmediateInterruptOK is true; turn it off
-		 * while messing with the NOTIFY state.  This prevents problems if
-		 * SIGINT or similar arrives while we're working.  Just to be real
-		 * sure, bump the interrupt holdoff counter as well.  That way, even
-		 * if something inside ProcessIncomingNotify() transiently sets
-		 * ImmediateInterruptOK (eg while waiting on a lock), we won't get
-		 * interrupted until we're done with the notify interrupt.
-		 */
-		ImmediateInterruptOK = false;
-		HOLD_INTERRUPTS();
+	/* signal that work needs to be done */
+	notifyInterruptPending = 1;
 
-		/*
-		 * I'm not sure whether some flavors of Unix might allow another
-		 * SIGUSR1 occurrence to recursively interrupt this routine. To cope
-		 * with the possibility, we do the same sort of dance that
-		 * EnableNotifyInterrupt must do --- see that routine for comments.
-		 */
-		notifyInterruptEnabled = 0;		/* disable any recursive signal */
-		notifyInterruptOccurred = 1;	/* do at least one iteration */
-		for (;;)
-		{
-			notifyInterruptEnabled = 1;
-			if (!notifyInterruptOccurred)
-				break;
-			notifyInterruptEnabled = 0;
-			if (notifyInterruptOccurred)
-			{
-				/* Here, it is finally safe to do stuff. */
-				if (Trace_notify)
-					elog(DEBUG1, "HandleNotifyInterrupt: perform async notify");
-
-				ProcessIncomingNotify();
-
-				if (Trace_notify)
-					elog(DEBUG1, "HandleNotifyInterrupt: done");
-			}
-		}
-
-		/*
-		 * Restore the holdoff level and ImmediateInterruptOK, and check for
-		 * interrupts if needed.
-		 */
-		RESUME_INTERRUPTS();
-		ImmediateInterruptOK = save_ImmediateInterruptOK;
-		if (save_ImmediateInterruptOK)
-			CHECK_FOR_INTERRUPTS();
-	}
-	else
-	{
-		/*
-		 * In this path it is NOT SAFE to do much of anything, except this:
-		 */
-		notifyInterruptOccurred = 1;
-	}
+	/* make sure the event is processed in due course */
+	/* XXX: arguably we should only do this if notifyInterruptOK */
+	if (MyProc != NULL)
+		SetLatch(&MyProc->procLatch);
 }
 
 /*
@@ -1723,44 +1664,11 @@ EnableNotifyInterrupt(void)
 	if (IsTransactionOrTransactionBlock())
 		return;					/* not really idle */
 
-	/*
-	 * This code is tricky because we are communicating with a signal handler
-	 * that could interrupt us at any point.  If we just checked
-	 * notifyInterruptOccurred and then set notifyInterruptEnabled, we could
-	 * fail to respond promptly to a signal that happens in between those two
-	 * steps.  (A very small time window, perhaps, but Murphy's Law says you
-	 * can hit it...)  Instead, we first set the enable flag, then test the
-	 * occurred flag.  If we see an unserviced interrupt has occurred, we
-	 * re-clear the enable flag before going off to do the service work. (That
-	 * prevents re-entrant invocation of ProcessIncomingNotify() if another
-	 * interrupt occurs.) If an interrupt comes in between the setting and
-	 * clearing of notifyInterruptEnabled, then it will have done the service
-	 * work and left notifyInterruptOccurred zero, so we have to check again
-	 * after clearing enable.  The whole thing has to be in a loop in case
-	 * another interrupt occurs while we're servicing the first. Once we get
-	 * out of the loop, enable is set and we know there is no unserviced
-	 * interrupt.
-	 *
-	 * NB: an overenthusiastic optimizing compiler could easily break this
-	 * code. Hopefully, they all understand what "volatile" means these days.
-	 */
-	for (;;)
+	while (notifyInterruptPending)
 	{
-		notifyInterruptEnabled = 1;
-		if (!notifyInterruptOccurred)
-			break;
-		notifyInterruptEnabled = 0;
-		if (notifyInterruptOccurred)
-		{
-			if (Trace_notify)
-				elog(DEBUG1, "EnableNotifyInterrupt: perform async notify");
-
-			ProcessIncomingNotify();
-
-			if (Trace_notify)
-				elog(DEBUG1, "EnableNotifyInterrupt: done");
-		}
+		ProcessIncomingNotify();
 	}
+	notifyInterruptOK = true;
 }
 
 /*
@@ -1777,13 +1685,23 @@ EnableNotifyInterrupt(void)
 bool
 DisableNotifyInterrupt(void)
 {
-	bool		result = (notifyInterruptEnabled != 0);
+	bool        result = (notifyInterruptOK != 0);
 
-	notifyInterruptEnabled = 0;
+	notifyInterruptOK = true;
 
 	return result;
 }
 
+void
+ProcessNotifyInterrupt(void)
+{
+	if (IsTransactionOrTransactionBlock())
+		return;					/* not really idle */
+
+	ProcessIncomingNotify();
+}
+
+
 /*
  * Read all pending notifications from the queue, and deliver appropriate
  * ones to my frontend.  Stop when we reach queue head or an uncommitted
@@ -2076,9 +1994,10 @@ asyncQueueAdvanceTail(void)
 /*
  * ProcessIncomingNotify
  *
- *		Deal with arriving NOTIFYs from other backends.
- *		This is called either directly from the PROCSIG_NOTIFY_INTERRUPT
- *		signal handler, or the next time control reaches the outer idle loop.
+ *		Deal with arriving NOTIFYs from other backends as soon as it's safe to
+ *		do so. This used to be called from the PROCSIG_NOTIFY_INTERRUPT
+ *		signal handler, but isn't anymore.
+ *
  *		Scan the queue for arriving notifications and report them to my front
  *		end.
  *
@@ -2087,18 +2006,13 @@ asyncQueueAdvanceTail(void)
 static void
 ProcessIncomingNotify(void)
 {
-	bool		catchup_enabled;
-
 	/* We *must* reset the flag */
-	notifyInterruptOccurred = 0;
+	notifyInterruptPending = 0;
 
 	/* Do nothing else if we aren't actively listening */
 	if (listenChannels == NIL)
 		return;
 
-	/* Must prevent catchup interrupt while I am running */
-	catchup_enabled = DisableCatchupInterrupt();
-
 	if (Trace_notify)
 		elog(DEBUG1, "ProcessIncomingNotify");
 
@@ -2123,9 +2037,6 @@ ProcessIncomingNotify(void)
 
 	if (Trace_notify)
 		elog(DEBUG1, "ProcessIncomingNotify: done");
-
-	if (catchup_enabled)
-		EnableCatchupInterrupt();
 }
 
 /*
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 59204cf..93e6c3b 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -74,9 +74,12 @@
 #endif
 #endif   /* USE_SSL */
 
+#include "miscadmin.h"
+
 #include "libpq/libpq.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
+#include "storage/proc.h"
 
 
 #ifdef USE_SSL
@@ -258,7 +261,7 @@ secure_read(Port *port, void *ptr, size_t len)
 	{
 		int			err;
 
-rloop:
+rloop_ssl:
 		errno = 0;
 		n = SSL_read(port->ssl, ptr, len);
 		err = SSL_get_error(port->ssl, n);
@@ -266,8 +269,23 @@ rloop:
 		{
 			case SSL_ERROR_NONE:
 				port->count += n;
+
+				prepare_for_client_read();
+				CHECK_FOR_INTERRUPTS();
+				client_read_ended();
+
 				break;
 			case SSL_ERROR_WANT_READ:
+				/*
+				 * We'll, among others, get here if the low level routine
+				 * doing the actual recv() via the socket got interrupted by a
+				 * interrupt. That's so we can handle interrupts once outside
+				 * openssl so we don't jump out from underneath its covers.
+				 */
+				prepare_for_client_read();
+				CHECK_FOR_INTERRUPTS();
+				client_read_ended();
+				/* fallthrough */
 			case SSL_ERROR_WANT_WRITE:
 				if (port->noblock)
 				{
@@ -275,13 +293,7 @@ rloop:
 					n = -1;
 					break;
 				}
-#ifdef WIN32
-				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
-											(err == SSL_ERROR_WANT_READ) ?
-									FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
-											INFINITE);
-#endif
-				goto rloop;
+				goto rloop_ssl;
 			case SSL_ERROR_SYSCALL:
 				/* leave it to caller to ereport the value of errno */
 				if (n != -1)
@@ -312,11 +324,33 @@ rloop:
 	else
 #endif
 	{
-		prepare_for_client_read();
-
+rloop_nossl:
+		/*
+		 * Try to read from the socket without blocking. If it suceeds we're
+		 * done, otherwise we'll wait for the socket using the latch mechanism.
+		 */
 		n = recv(port->sock, ptr, len, 0);
 
+		/* process interrupts that potentially happened while receiving */
+		prepare_for_client_read();
+		CHECK_FOR_INTERRUPTS();
 		client_read_ended();
+
+		if (!port->noblock && n <= 0 && errno == EWOULDBLOCK)
+		{
+			int w;
+
+			w = WaitLatchOrSocket(&MyProc->procLatch,
+								  WL_LATCH_SET | WL_SOCKET_READABLE,
+								  port->sock, 0);
+
+			if (w & (WL_SOCKET_READABLE | WL_LATCH_SET))
+			{
+				ResetLatch(&MyProc->procLatch);
+				goto rloop_nossl;
+			}
+		}
+
 	}
 
 	return n;
@@ -386,7 +420,7 @@ secure_write(Port *port, void *ptr, size_t len)
 			}
 		}
 
-wloop:
+wloop_ssl:
 		errno = 0;
 		n = SSL_write(port->ssl, ptr, len);
 		err = SSL_get_error(port->ssl, n);
@@ -397,13 +431,15 @@ wloop:
 				break;
 			case SSL_ERROR_WANT_READ:
 			case SSL_ERROR_WANT_WRITE:
-#ifdef WIN32
-				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
-											(err == SSL_ERROR_WANT_READ) ?
-									FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
-											INFINITE);
-#endif
-				goto wloop;
+				if (MyProc != NULL)
+				{
+					/* XXX: we probably want to process latch sets at some point here */
+					WaitLatchOrSocket(&MyProc->procLatch,
+									  err == SSL_ERROR_WANT_READ ?
+									  WL_SOCKET_READABLE :  WL_SOCKET_WRITEABLE,
+									  port->sock, 0);
+				}
+				goto wloop_ssl;
 			case SSL_ERROR_SYSCALL:
 				/* leave it to caller to ereport the value of errno */
 				if (n != -1)
@@ -455,8 +491,28 @@ wloop:
 	}
 	else
 #endif
+	{
+wloop_nossl:
 		n = send(port->sock, ptr, len, 0);
 
+		if (MyProc == NULL)
+			goto wloop_nossl;
+
+		if (n < 0 && errno == EWOULDBLOCK && MyProc != NULL)
+		{
+			int w;
+
+			/* XXX: we probably want to process latch sets at some point here */
+			w = WaitLatchOrSocket(&MyProc->procLatch,
+								  WL_SOCKET_WRITEABLE,
+								  port->sock, 0);
+			if (w & (WL_SOCKET_WRITEABLE | WL_LATCH_SET))
+			{
+				ResetLatch(&MyProc->procLatch);
+				goto wloop_nossl;
+			}
+		}
+	}
 	return n;
 }
 
@@ -487,13 +543,40 @@ my_sock_read(BIO *h, char *buf, int size)
 {
 	int			res = 0;
 
-	prepare_for_client_read();
-
 	if (buf != NULL)
 	{
-		res = recv(h->num, buf, size, 0);
 		BIO_clear_retry_flags(h);
-		if (res <= 0)
+retry:
+		res = recv(h->num, buf, size, 0);
+
+		if (MyProc == NULL && res < 0 && errno == EWOULDBLOCK)
+			goto retry;
+		else if (!MyProcPort->noblock && res < 0 && errno == EWOULDBLOCK)
+		{
+			int w;
+
+			w = WaitLatchOrSocket(&MyProc->procLatch,
+								  WL_LATCH_SET | WL_SOCKET_READABLE,
+								  h->num, 0);
+
+			/*
+			 * If the latch has been set while we were waiting for the socket
+			 * to become readable force a return to outside openssl's
+			 * control. There we can correctly do error handling and such
+			 * without disturbing openssl's internal state.
+			 */
+			if (w & WL_LATCH_SET)
+			{
+				ResetLatch(&MyProc->procLatch);
+				BIO_set_retry_read(h);
+				errno = EINTR;
+				return -1;
+			}
+
+			if (w & WL_SOCKET_READABLE)
+				goto retry;
+		}
+		else if (res <= 0)
 		{
 			/* If we were interrupted, tell caller to retry */
 			if (errno == EINTR)
@@ -503,8 +586,6 @@ my_sock_read(BIO *h, char *buf, int size)
 		}
 	}
 
-	client_read_ended();
-
 	return res;
 }
 
@@ -517,7 +598,7 @@ my_sock_write(BIO *h, const char *buf, int size)
 	BIO_clear_retry_flags(h);
 	if (res <= 0)
 	{
-		if (errno == EINTR)
+		if (errno == EINTR || errno == EWOULDBLOCK)
 		{
 			BIO_set_retry_write(h);
 		}
@@ -1012,12 +1093,12 @@ aloop:
 		{
 			case SSL_ERROR_WANT_READ:
 			case SSL_ERROR_WANT_WRITE:
-#ifdef WIN32
-				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
-											(err == SSL_ERROR_WANT_READ) ?
-						FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
-											INFINITE);
-#endif
+				/* FIXME: interrupt handling? */
+				if (MyProc != NULL)
+					WaitLatchOrSocket(&MyProc->procLatch,
+									  err == SSL_ERROR_WANT_READ ?
+									  WL_SOCKET_READABLE :  WL_SOCKET_WRITEABLE,
+									  port->sock, 0);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 605d891..cd4a941 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -158,6 +158,22 @@ pq_init(void)
 	PqCommBusy = false;
 	DoingCopyOut = false;
 	on_proc_exit(pq_close, 0);
+
+	/*
+	 * We now always operate the underlying socket in nonblocking mode and use
+	 * WaitLatchOrSocket to implement blocking semantics if needed.
+	 *
+	 * Use COMMERROR on failure, because ERROR would try to send the error to
+	 * the client, which might require changing the mode again, leading to
+	 * infinite recursion.
+	 */
+#ifdef WIN32
+	pgwin32_noblock = true;
+#else
+	if (!pg_set_noblock(MyProcPort->sock))
+		ereport(COMMERROR,
+				(errmsg("could not set socket to nonblocking mode: %m")));
+#endif
 }
 
 /* --------------------------------
@@ -792,31 +808,6 @@ TouchSocketFiles(void)
 static void
 pq_set_nonblocking(bool nonblocking)
 {
-	if (MyProcPort->noblock == nonblocking)
-		return;
-
-#ifdef WIN32
-	pgwin32_noblock = nonblocking ? 1 : 0;
-#else
-
-	/*
-	 * Use COMMERROR on failure, because ERROR would try to send the error to
-	 * the client, which might require changing the mode again, leading to
-	 * infinite recursion.
-	 */
-	if (nonblocking)
-	{
-		if (!pg_set_noblock(MyProcPort->sock))
-			ereport(COMMERROR,
-					(errmsg("could not set socket to nonblocking mode: %m")));
-	}
-	else
-	{
-		if (!pg_set_block(MyProcPort->sock))
-			ereport(COMMERROR,
-					(errmsg("could not set socket to blocking mode: %m")));
-	}
-#endif
 	MyProcPort->noblock = nonblocking;
 }
 
diff --git a/src/backend/port/unix_latch.c b/src/backend/port/unix_latch.c
index d0e928f..7d9bcca 100644
--- a/src/backend/port/unix_latch.c
+++ b/src/backend/port/unix_latch.c
@@ -199,10 +199,6 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
 /*
  * Like WaitLatch, but with an extra socket argument for WL_SOCKET_*
  * conditions.
- *
- * When waiting on a socket, WL_SOCKET_READABLE *must* be included in
- * 'wakeEvents'; WL_SOCKET_WRITEABLE is optional.  The reason for this is
- * that EOF and error conditions are reported only via WL_SOCKET_READABLE.
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
@@ -230,8 +226,6 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		wakeEvents &= ~(WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);
 
 	Assert(wakeEvents != 0);	/* must have at least one wake event */
-	/* Cannot specify WL_SOCKET_WRITEABLE without WL_SOCKET_READABLE */
-	Assert((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) != WL_SOCKET_WRITEABLE);
 
 	if ((wakeEvents & WL_LATCH_SET) && latch->owner_pid != MyProcPid)
 		elog(ERROR, "cannot wait on a latch owned by another process");
@@ -352,7 +346,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 				result |= WL_SOCKET_READABLE;
 			}
 			if ((wakeEvents & WL_SOCKET_WRITEABLE) &&
-				(pfds[0].revents & POLLOUT))
+				(pfds[0].revents & (POLLOUT | POLLHUP | POLLERR | POLLNVAL)))
 			{
 				result |= WL_SOCKET_WRITEABLE;
 			}
diff --git a/src/backend/port/win32_latch.c b/src/backend/port/win32_latch.c
index 6c50dbb..95e4a16 100644
--- a/src/backend/port/win32_latch.c
+++ b/src/backend/port/win32_latch.c
@@ -117,8 +117,6 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		wakeEvents &= ~(WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);
 
 	Assert(wakeEvents != 0);	/* must have at least one wake event */
-	/* Cannot specify WL_SOCKET_WRITEABLE without WL_SOCKET_READABLE */
-	Assert((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) != WL_SOCKET_WRITEABLE);
 
 	if ((wakeEvents & WL_LATCH_SET) && latch->owner_pid != MyProcPid)
 		elog(ERROR, "cannot wait on a latch owned by another process");
@@ -152,10 +150,10 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 	if (wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE))
 	{
 		/* Need an event object to represent events on the socket */
-		int			flags = 0;
+		int			flags = FD_CLOSE;
 
 		if (wakeEvents & WL_SOCKET_READABLE)
-			flags |= (FD_READ | FD_CLOSE);
+			flags |= FD_READ;
 		if (wakeEvents & WL_SOCKET_WRITEABLE)
 			flags |= FD_WRITE;
 
@@ -232,7 +230,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 				elog(ERROR, "failed to enumerate network events: error code %u",
 					 WSAGetLastError());
 			if ((wakeEvents & WL_SOCKET_READABLE) &&
-				(resEvents.lNetworkEvents & (FD_READ | FD_CLOSE)))
+				(resEvents.lNetworkEvents & FD_READ))
 			{
 				result |= WL_SOCKET_READABLE;
 			}
@@ -241,6 +239,11 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 			{
 				result |= WL_SOCKET_WRITEABLE;
 			}
+			if (resEvents.lNetworkEvents & FD_CLOSE)
+			{
+				result |= WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE;
+			}
+
 		}
 		else if ((wakeEvents & WL_POSTMASTER_DEATH) &&
 				 rc == WAIT_OBJECT_0 + pmdeath_eventno)
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index b53cfdb..1549cf0 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -614,6 +614,10 @@ AutoVacLauncherMain(int argc, char *argv[])
 
 		ResetLatch(&MyProc->procLatch);
 
+		/* process catchup interrupts and the like */
+		/* FIXME: Will be ignored by ProcessInterrupts right now */
+		CHECK_FOR_INTERRUPTS();
+
 		DisableCatchupInterrupt();
 
 		/*
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index d7d0406..a4c9c51 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -18,6 +18,7 @@
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "storage/ipc.h"
+#include "storage/proc.h"
 #include "storage/sinvaladt.h"
 #include "utils/inval.h"
 
@@ -41,8 +42,8 @@ uint64		SharedInvalidMessageCounter;
  * does not grok "volatile", you'd be best advised to compile this file
  * with all optimization turned off.
  */
-static volatile int catchupInterruptEnabled = 0;
-static volatile int catchupInterruptOccurred = 0;
+volatile sig_atomic_t catchupInterruptOK = 0;
+volatile sig_atomic_t catchupInterruptPending = 0;
 
 static void ProcessCatchupEvent(void);
 
@@ -141,9 +142,9 @@ ReceiveSharedInvalidMessages(
 	 * catchup signal this way avoids creating spikes in system load for what
 	 * should be just a background maintenance activity.
 	 */
-	if (catchupInterruptOccurred)
+	if (catchupInterruptPending)
 	{
-		catchupInterruptOccurred = 0;
+		catchupInterruptPending = 0;
 		elog(DEBUG4, "sinval catchup complete, cleaning queue");
 		SICleanupQueue(false, 0);
 	}
@@ -155,12 +156,9 @@ ReceiveSharedInvalidMessages(
  *
  * This is called when PROCSIG_CATCHUP_INTERRUPT is received.
  *
- * If we are idle (catchupInterruptEnabled is set), we can safely
- * invoke ProcessCatchupEvent directly.  Otherwise, just set a flag
- * to do it later.  (Note that it's quite possible for normal processing
- * of the current transaction to cause ReceiveSharedInvalidMessages()
- * to be run later on; in that case the flag will get cleared again,
- * since there's no longer any reason to do anything.)
+ * We used to directly call ProcessCatchupEvent directly when idle. These days
+ * we just set a flag to do it later and notify the process of that fact by
+ * setting the processes latch.
  */
 void
 HandleCatchupInterrupt(void)
@@ -170,63 +168,19 @@ HandleCatchupInterrupt(void)
 	 * you do here.
 	 */
 
-	/* Don't joggle the elbow of proc_exit */
-	if (proc_exit_inprogress)
-		return;
+	catchupInterruptPending = 1;
 
-	if (catchupInterruptEnabled)
-	{
-		bool		save_ImmediateInterruptOK = ImmediateInterruptOK;
-
-		/*
-		 * We may be called while ImmediateInterruptOK is true; turn it off
-		 * while messing with the catchup state.  This prevents problems if
-		 * SIGINT or similar arrives while we're working.  Just to be real
-		 * sure, bump the interrupt holdoff counter as well.  That way, even
-		 * if something inside ProcessCatchupEvent() transiently sets
-		 * ImmediateInterruptOK (eg while waiting on a lock), we won't get
-		 * interrupted until we're done with the catchup interrupt.
-		 */
-		ImmediateInterruptOK = false;
-		HOLD_INTERRUPTS();
-
-		/*
-		 * I'm not sure whether some flavors of Unix might allow another
-		 * SIGUSR1 occurrence to recursively interrupt this routine. To cope
-		 * with the possibility, we do the same sort of dance that
-		 * EnableCatchupInterrupt must do --- see that routine for comments.
-		 */
-		catchupInterruptEnabled = 0;	/* disable any recursive signal */
-		catchupInterruptOccurred = 1;	/* do at least one iteration */
-		for (;;)
-		{
-			catchupInterruptEnabled = 1;
-			if (!catchupInterruptOccurred)
-				break;
-			catchupInterruptEnabled = 0;
-			if (catchupInterruptOccurred)
-			{
-				/* Here, it is finally safe to do stuff. */
-				ProcessCatchupEvent();
-			}
-		}
+	/* make sure the event is processed in due course */
+	/* XXX: arguably we should only do this if notifyInterruptEnabled */
+	if (MyProc != NULL)
+		SetLatch(&MyProc->procLatch);
+}
 
-		/*
-		 * Restore the holdoff level and ImmediateInterruptOK, and check for
-		 * interrupts if needed.
-		 */
-		RESUME_INTERRUPTS();
-		ImmediateInterruptOK = save_ImmediateInterruptOK;
-		if (save_ImmediateInterruptOK)
-			CHECK_FOR_INTERRUPTS();
-	}
-	else
-	{
-		/*
-		 * In this path it is NOT SAFE to do much of anything, except this:
-		 */
-		catchupInterruptOccurred = 1;
-	}
+void
+ProcessCatchupInterrupt(void)
+{
+	/* Here, it is finally safe to do stuff. */
+	ProcessCatchupEvent();
 }
 
 /*
@@ -242,35 +196,13 @@ HandleCatchupInterrupt(void)
 void
 EnableCatchupInterrupt(void)
 {
-	/*
-	 * This code is tricky because we are communicating with a signal handler
-	 * that could interrupt us at any point.  If we just checked
-	 * catchupInterruptOccurred and then set catchupInterruptEnabled, we could
-	 * fail to respond promptly to a signal that happens in between those two
-	 * steps.  (A very small time window, perhaps, but Murphy's Law says you
-	 * can hit it...)  Instead, we first set the enable flag, then test the
-	 * occurred flag.  If we see an unserviced interrupt has occurred, we
-	 * re-clear the enable flag before going off to do the service work. (That
-	 * prevents re-entrant invocation of ProcessCatchupEvent() if another
-	 * interrupt occurs.) If an interrupt comes in between the setting and
-	 * clearing of catchupInterruptEnabled, then it will have done the service
-	 * work and left catchupInterruptOccurred zero, so we have to check again
-	 * after clearing enable.  The whole thing has to be in a loop in case
-	 * another interrupt occurs while we're servicing the first. Once we get
-	 * out of the loop, enable is set and we know there is no unserviced
-	 * interrupt.
-	 *
-	 * NB: an overenthusiastic optimizing compiler could easily break this
-	 * code. Hopefully, they all understand what "volatile" means these days.
-	 */
+	catchupInterruptOK = true;
+
 	for (;;)
 	{
-		catchupInterruptEnabled = 1;
-		if (!catchupInterruptOccurred)
+		if (!catchupInterruptPending)
 			break;
-		catchupInterruptEnabled = 0;
-		if (catchupInterruptOccurred)
-			ProcessCatchupEvent();
+		ProcessCatchupEvent();
 	}
 }
 
@@ -288,9 +220,9 @@ EnableCatchupInterrupt(void)
 bool
 DisableCatchupInterrupt(void)
 {
-	bool		result = (catchupInterruptEnabled != 0);
+	bool        result = (catchupInterruptOK != 0);
 
-	catchupInterruptEnabled = 0;
+	catchupInterruptOK = false;
 
 	return result;
 }
@@ -299,24 +231,15 @@ DisableCatchupInterrupt(void)
  * ProcessCatchupEvent
  *
  * Respond to a catchup event (PROCSIG_CATCHUP_INTERRUPT) from another
- * backend.
- *
- * This is called either directly from the PROCSIG_CATCHUP_INTERRUPT
- * signal handler, or the next time control reaches the outer idle loop
- * (assuming there's still anything to do by then).
+ * backend once it's safe to do so.
  */
 static void
 ProcessCatchupEvent(void)
 {
-	bool		notify_enabled;
-
-	/* Must prevent notify interrupt while I am running */
-	notify_enabled = DisableNotifyInterrupt();
-
 	/*
 	 * What we need to do here is cause ReceiveSharedInvalidMessages() to run,
 	 * which will do the necessary work and also reset the
-	 * catchupInterruptOccurred flag.  If we are inside a transaction we can
+	 * catchupInterruptPending flag.  If we are inside a transaction we can
 	 * just call AcceptInvalidationMessages() to do this.  If we aren't, we
 	 * start and immediately end a transaction; the call to
 	 * AcceptInvalidationMessages() happens down inside transaction start.
@@ -337,7 +260,4 @@ ProcessCatchupEvent(void)
 		StartTransactionCommand();
 		CommitTransactionCommand();
 	}
-
-	if (notify_enabled)
-		EnableNotifyInterrupt();
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7b5480f..d9d5ddf 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -490,13 +490,11 @@ ReadCommand(StringInfo inBuf)
  * prepare_for_client_read -- set up to possibly block on client input
  *
  * This must be called immediately before any low-level read from the
- * client connection.  It is necessary to do it at a sufficiently low level
- * that there won't be any other operations except the read kernel call
- * itself between this call and the subsequent client_read_ended() call.
- * In particular there mustn't be use of malloc() or other potentially
- * non-reentrant libc functions.  This restriction makes it safe for us
- * to allow interrupt service routines to execute nontrivial code while
- * we are waiting for input.
+ * client connection.
+ *
+ * NB: We used to allow direct execution of interrupt handlers
+ * (i.e. ImmediateInterruptOK = true) once within, but that proved ot be
+ * fragile and complicated.
  */
 void
 prepare_for_client_read(void)
@@ -507,9 +505,6 @@ prepare_for_client_read(void)
 		EnableNotifyInterrupt();
 		EnableCatchupInterrupt();
 
-		/* Allow cancel/die interrupts to be processed while waiting */
-		ImmediateInterruptOK = true;
-
 		/* And don't forget to detect one that already arrived */
 		CHECK_FOR_INTERRUPTS();
 	}
@@ -2588,8 +2583,8 @@ die(SIGNAL_ARGS)
 		ProcDiePending = true;
 
 		/*
-		 * If it's safe to interrupt, and we're waiting for input or a lock,
-		 * service the interrupt immediately
+		 * If it's safe to interrupt, and we're waiting for a lock, service
+		 * the interrupt immediately
 		 */
 		if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
 			CritSectionCount == 0)
@@ -2630,8 +2625,8 @@ StatementCancelHandler(SIGNAL_ARGS)
 		QueryCancelPending = true;
 
 		/*
-		 * If it's safe to interrupt, and we're waiting for input or a lock,
-		 * service the interrupt immediately
+		 * If it's safe to interrupt, and we're waiting for a lock, service
+		 * the interrupt immediately
 		 */
 		if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
 			CritSectionCount == 0)
@@ -2789,8 +2784,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 			RecoveryConflictRetryable = false;
 
 		/*
-		 * If it's safe to interrupt, and we're waiting for input or a lock,
-		 * service the interrupt immediately
+		 * If it's safe to interrupt, and we're waiting for a lock, service
+		 * the interrupt immediately
 		 */
 		if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
 			CritSectionCount == 0)
@@ -2966,6 +2961,22 @@ ProcessInterrupts(void)
 					 errmsg("canceling statement due to user request")));
 		}
 	}
+
+	/*
+	 * Process catchup/notify events if we're up for doing so right now. If
+	 * not they'll get run the next time the respective interrupt processing
+	 * is enabled. Thus it's okay to consume the interrupt despite not
+	 * processing these events.
+	 */
+	if (catchupInterruptPending && catchupInterruptOK)
+	{
+		ProcessCatchupInterrupt();
+	}
+	if (notifyInterruptPending && notifyInterruptOK)
+	{
+		ProcessNotifyInterrupt();
+	}
+
 	/* If we get here, do nothing (probably, QueryCancelPending was reset) */
 }
 
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index 0650e65..cd77fda 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -13,6 +13,8 @@
 #ifndef ASYNC_H
 #define ASYNC_H
 
+#include <signal.h>
+
 #include "fmgr.h"
 
 /*
@@ -21,6 +23,8 @@
 #define NUM_ASYNC_BUFFERS	8
 
 extern bool Trace_notify;
+extern volatile sig_atomic_t notifyInterruptOK;
+extern volatile sig_atomic_t notifyInterruptPending;
 
 extern Size AsyncShmemSize(void);
 extern void AsyncShmemInit(void);
@@ -55,5 +59,6 @@ extern void HandleNotifyInterrupt(void);
  */
 extern void EnableNotifyInterrupt(void);
 extern bool DisableNotifyInterrupt(void);
+extern void ProcessNotifyInterrupt(void);
 
 #endif   /* ASYNC_H */
diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h
index 812ea95..b36be97 100644
--- a/src/include/storage/sinval.h
+++ b/src/include/storage/sinval.h
@@ -14,8 +14,9 @@
 #ifndef SINVAL_H
 #define SINVAL_H
 
-#include "storage/relfilenode.h"
+#include <signal.h>
 
+#include "storage/relfilenode.h"
 
 /*
  * We support several types of shared-invalidation messages:
@@ -123,6 +124,8 @@ typedef union
 /* Counter of messages processed; don't worry about overflow. */
 extern uint64 SharedInvalidMessageCounter;
 
+extern volatile sig_atomic_t catchupInterruptOK;
+extern volatile sig_atomic_t catchupInterruptPending;
 
 extern void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs,
 						  int n);
@@ -140,6 +143,7 @@ extern void HandleCatchupInterrupt(void);
  */
 extern void EnableCatchupInterrupt(void);
 extern bool DisableCatchupInterrupt(void);
+extern void ProcessCatchupInterrupt(void);
 
 extern int xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
 									 bool *RelcacheInitFileInval);
