From 47d2db3265f561f899206dfc5c671332af3ab7f2 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Tue, 18 Sep 2018 12:30:18 +1200
Subject: [PATCH] Allow DSM allocation to be interrupted.

Chris Travers reported that the startup process can repeatedly try to
cancel a backend that is in a posix_fallocate()/EINTR loop and cause it
to loop forever.  Teach the retry loop to give up if an interrupt is
pending.  Don't actually check for interrupts in that loop though,
because a non-local exit would skip some clean-up code in the caller.

Back-patch to 9.4 where DSM was added (and posix_fallocate() was later
back-patched).

Author: Chris Travers
Reviewed-by: Ildar Musin, Murak Kabilov, Oleksii Kliukin
Tested-by: Oleksii Kliukin
Discussion: https://postgr.es/m/CAN-RpxB-oeZve_J3SM_6%3DHXPmvEG%3DHX%2B9V9pi8g2YR7YW0rBBg%40mail.gmail.com
---
 src/backend/storage/ipc/dsm_impl.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/src/backend/storage/ipc/dsm_impl.c b/src/backend/storage/ipc/dsm_impl.c
index 77e1bab54b..70f899e765 100644
--- a/src/backend/storage/ipc/dsm_impl.c
+++ b/src/backend/storage/ipc/dsm_impl.c
@@ -47,6 +47,7 @@
  */
 
 #include "postgres.h"
+#include "miscadmin.h"
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -330,6 +331,14 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
 			shm_unlink(name);
 		errno = save_errno;
 
+		/*
+		 * If we received a query cancel or termination signal, we will have
+		 * EINTR set here.  If the caller said that errors are OK here, check
+		 * for interrupts immediately.
+		 */
+		if (errno == EINTR && elevel >= ERROR)
+			CHECK_FOR_INTERRUPTS();
+
 		ereport(elevel,
 				(errcode_for_dynamic_shared_memory(),
 				 errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
@@ -419,11 +428,15 @@ dsm_impl_posix_resize(int fd, off_t size)
 #if defined(HAVE_POSIX_FALLOCATE) && defined(__linux__)
 	if (rc == 0)
 	{
-		/* We may get interrupted, if so just retry. */
+		/*
+		 * We may get interrupted.  If so, just retry unless there is an
+		 * interrupt pending.  This avoids the possibility of looping forever
+		 * if another backend is repeatedly trying to interrupt us.
+		 */
 		do
 		{
 			rc = posix_fallocate(fd, 0, size);
-		} while (rc == EINTR);
+		} while (rc == EINTR && !(ProcDiePending || QueryCancelPending));
 
 		/*
 		 * The caller expects errno to be set, but posix_fallocate() doesn't
-- 
2.17.0

