From 400fed6311086fc7fec4463fe5c093c0931cc5ba Mon Sep 17 00:00:00 2001
From: Thomas Munro <munro@ip9.org>
Date: Wed, 18 Apr 2018 00:59:39 +0100
Subject: [PATCH 1/2] Use signals for postmaster death on Linux.

Linux provides a way to ask for a signal when your parent process dies.  Use
that to make PostmasterIsAlive() very cheap.

Author: Thomas Munro, based on a suggestion from Andres Freund
Reviewed-By: Michael Paquier
Discussion: https://postgr.es/m/7261eb39-0369-f2f4-1bb5-62f3b6083b5e%40iki.fi
            https://postgr.es/m/20180411002643.6buofht4ranhei7k%40alap3.anarazel.de
---
 configure                          | 40 +++++++++++++++++++++++++++
 configure.in                       | 22 +++++++++++++++
 src/backend/storage/ipc/latch.c    |  6 ++---
 src/backend/storage/ipc/pmsignal.c | 55 +++++++++++++++++++++++++++++++++++++-
 src/backend/utils/init/miscinit.c  |  4 +++
 src/include/c.h                    | 11 ++++++++
 src/include/pg_config.h.in         |  3 +++
 src/include/storage/pmsignal.h     | 17 +++++++++++-
 8 files changed, 153 insertions(+), 5 deletions(-)

diff --git a/configure b/configure
index 56f18dfbc26..29ba38ee174 100755
--- a/configure
+++ b/configure
@@ -9717,6 +9717,46 @@ program to use during the build." "$LINENO" 5
   fi
 fi
 
+#
+# Signals to detect parent process death
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for parent death signal support" >&5
+$as_echo_n "checking for parent death signal support... " >&6; }
+
+# Linux has prctl(PR_SET_PDEATHSIG, ...)
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <sys/prctl.h>
+
+int
+main ()
+{
+
+#ifndef PR_SET_PDEATHSIG
+#error PR_SET_PDEATHSIG not defined
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  HAVE_PR_SET_PDEATHSIG=1
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test x"$HAVE_PR_SET_PDEATHSIG" = "x1"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: PR_SET_PDEATHSIG" >&5
+$as_echo "PR_SET_PDEATHSIG" >&6; }
+
+$as_echo "#define HAVE_PR_SET_PDEATHSIG 1" >>confdefs.h
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
 #
 # Pthreads
 #
diff --git a/configure.in b/configure.in
index da02a56ec66..31dda503628 100644
--- a/configure.in
+++ b/configure.in
@@ -1022,6 +1022,28 @@ program to use during the build.])
   fi
 fi
 
+#
+# Signals to detect parent process death
+#
+AC_MSG_CHECKING([for parent death signal support])
+
+# Linux has prctl(PR_SET_PDEATHSIG, ...)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <sys/prctl.h>
+], [
+#ifndef PR_SET_PDEATHSIG
+#error PR_SET_PDEATHSIG not defined
+#endif
+])], [HAVE_PR_SET_PDEATHSIG=1])
+
+if test x"$HAVE_PR_SET_PDEATHSIG" = "x1"; then
+  AC_MSG_RESULT(PR_SET_PDEATHSIG)
+  AC_DEFINE(HAVE_PR_SET_PDEATHSIG, 1,
+            [Define to 1 if the system supports PR_SET_PDEATHSIG])
+else
+  AC_MSG_RESULT(no)
+fi
+
 #
 # Pthreads
 #
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index e6706f7fb80..f6dda9cc9ac 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -1112,7 +1112,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 			 * WL_POSTMASTER_DEATH event would be painful. Re-checking doesn't
 			 * cost much.
 			 */
-			if (!PostmasterIsAlive())
+			if (!PostmasterIsAliveInternal())
 			{
 				occurred_events->fd = PGINVALID_SOCKET;
 				occurred_events->events = WL_POSTMASTER_DEATH;
@@ -1230,7 +1230,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 			 * WL_POSTMASTER_DEATH event would be painful. Re-checking doesn't
 			 * cost much.
 			 */
-			if (!PostmasterIsAlive())
+			if (!PostmasterIsAliveInternal())
 			{
 				occurred_events->fd = PGINVALID_SOCKET;
 				occurred_events->events = WL_POSTMASTER_DEATH;
@@ -1390,7 +1390,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 		 * even though there is no known reason to think that the event could
 		 * be falsely set on Windows.
 		 */
-		if (!PostmasterIsAlive())
+		if (!PostmasterIsAliveInternal())
 		{
 			occurred_events->fd = PGINVALID_SOCKET;
 			occurred_events->events = WL_POSTMASTER_DEATH;
diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c
index be61858fc67..082da9351be 100644
--- a/src/backend/storage/ipc/pmsignal.c
+++ b/src/backend/storage/ipc/pmsignal.c
@@ -23,6 +23,9 @@
 #include "storage/pmsignal.h"
 #include "storage/shmem.h"
 
+#if defined(HAVE_PR_SET_PDEATHSIG)
+#include <sys/prctl.h>
+#endif
 
 /*
  * The postmaster is signaled by its children by sending SIGUSR1.  The
@@ -71,6 +74,28 @@ struct PMSignalData
 
 NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
 
+#ifdef USE_POSTMASTER_DEATH_SIGNAL
+sig_atomic_t postmaster_possibly_dead = false;
+
+static void
+postmaster_death_handler(int signo)
+{
+	postmaster_possibly_dead = true;
+}
+
+/*
+ * The available signals depends on the OS and architecture.  SIGUSR1 and
+ * SIGUSR2 are already used for other things, so choose another one.
+ */
+#if defined(SIGINFO)
+#define POSTMASTER_DEATH_SIGNAL SIGINFO
+#elif defined(SIGPWR)
+#define POSTMASTER_DEATH_SIGNAL SIGPWR
+#else
+#error "cannot find a signal to use for postmaster death"
+#endif
+
+#endif
 
 /*
  * PMSignalShmemSize
@@ -269,7 +294,7 @@ MarkPostmasterChildInactive(void)
  * PostmasterIsAlive - check whether postmaster process is still alive
  */
 bool
-PostmasterIsAlive(void)
+PostmasterIsAliveInternal(void)
 {
 #ifndef WIN32
 	char		c;
@@ -291,3 +316,31 @@ PostmasterIsAlive(void)
 	return (WaitForSingleObject(PostmasterHandle, 0) == WAIT_TIMEOUT);
 #endif							/* WIN32 */
 }
+
+/*
+ * PostmasterDeathSignalInit - request signal on postmaster death if possible
+ */
+void
+PostmasterDeathSignalInit(void)
+{
+#ifdef USE_POSTMASTER_DEATH_SIGNAL
+	int signum;
+
+	/* Register our signal handler. */
+	signum = POSTMASTER_DEATH_SIGNAL;
+	pqsignal(signum, postmaster_death_handler);
+
+	/* Request a signal on parent exit. */
+#ifdef HAVE_PR_SET_PDEATHSIG
+	if (prctl(PR_SET_PDEATHSIG, signum) < 0)
+		elog(ERROR, "could not request parent death signal: %m");
+#endif
+
+	/*
+	 * Just in case the parent was gone already and we missed it, we'd
+	 * better check the slow way.
+	 */
+	if (!PostmasterIsAliveInternal())
+		postmaster_possibly_dead = true;
+#endif
+}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 03b28c3604a..4bb28938c27 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -43,6 +43,7 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/pg_shmem.h"
+#include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/builtins.h"
@@ -304,6 +305,9 @@ InitPostmasterChild(void)
 	if (setsid() < 0)
 		elog(FATAL, "setsid() failed: %m");
 #endif
+
+	/* Request a signal if the postmaster dies, if possible. */
+	PostmasterDeathSignalInit();
 }
 
 /*
diff --git a/src/include/c.h b/src/include/c.h
index 95e9aeded9d..116c2a24814 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1155,6 +1155,17 @@ extern int	fdatasync(int fildes);
 #define NON_EXEC_STATIC static
 #endif
 
+/*
+ * Do we have a way to ask for a signal on parent death?  Build with
+ * NO_POSTMASTER_DEATH_SIGNAL defined to inhibit the use of death signals on
+ * platforms that support it, for testing purposes.
+ */
+#ifndef NO_POSTMASTER_DEATH_SIGNAL
+#if defined(HAVE_PR_SET_PDEATHSIG)
+#define USE_POSTMASTER_DEATH_SIGNAL
+#endif
+#endif
+
 /* /port compatibility functions */
 #include "port.h"
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index f3620231a71..5454f0d4807 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -428,6 +428,9 @@
 /* Define to 1 if the assembler supports PPC's LWARX mutex hint bit. */
 #undef HAVE_PPC_LWARX_MUTEX_HINT
 
+/* Define to 1 if the system supports PR_SET_PDEATHSIG */
+#undef HAVE_PR_SET_PDEATHSIG
+
 /* Define to 1 if you have the `pstat' function. */
 #undef HAVE_PSTAT
 
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index bec162cc16d..d55e30e4156 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -51,6 +51,21 @@ extern bool IsPostmasterChildWalSender(int slot);
 extern void MarkPostmasterChildActive(void);
 extern void MarkPostmasterChildInactive(void);
 extern void MarkPostmasterChildWalSender(void);
-extern bool PostmasterIsAlive(void);
+extern bool PostmasterIsAliveInternal(void);
+extern void PostmasterDeathSignalInit(void);
+
+#ifdef USE_POSTMASTER_DEATH_SIGNAL
+extern sig_atomic_t postmaster_possibly_dead;
+#endif
+
+static inline bool
+PostmasterIsAlive(void)
+{
+#ifdef USE_POSTMASTER_DEATH_SIGNAL
+	if (likely(!postmaster_possibly_dead))
+		return true;
+#endif
+	return PostmasterIsAliveInternal();
+}
 
 #endif							/* PMSIGNAL_H */
-- 
2.16.2

