w32attach-retry-block-v1.patch

text/plain

Filename: w32attach-retry-block-v1.patch
Type: text/plain
Part: 0
Message: Re: "could not reattach to shared memory" on buildfarm member dory

Patch

Format: unified
Series: patch v1
File+
src/backend/port/win32_shmem.c 18 1
src/backend/postmaster/postmaster.c 93 2
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index f8ca52e..e96517f 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -383,6 +383,20 @@ PGSharedMemoryReAttach(void)
 	Assert(UsedShmemSegAddr != NULL);
 	Assert(IsUnderPostmaster);
 
+#ifdef REATTACH_FORCE_FAIL_PERCENT
+
+	/*
+	 * For testing, emulate a system where my MapViewOfFileEx() fails some
+	 * percentage of the time.
+	 */
+	srandom((unsigned int) (MyProcPid ^ MyStartTime));
+	if (random() % 100 < (REATTACH_FORCE_FAIL_PERCENT))
+	{
+		elog(LOG, "emulating failure to reattach to shared memory");
+		proc_exit(4);
+	}
+#endif
+
 	/*
 	 * Release memory region reservation that was made by the postmaster
 	 */
@@ -392,8 +406,11 @@ PGSharedMemoryReAttach(void)
 
 	hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
 	if (!hdr)
-		elog(FATAL, "could not reattach to shared memory (key=%p, addr=%p): error code %lu",
+	{
+		elog(LOG, "could not reattach to shared memory (key=%p, addr=%p): error code %lu",
 			 UsedShmemSegID, UsedShmemSegAddr, GetLastError());
+		proc_exit(4);			/* Ask internal_forkexec() to retry. */
+	}
 	if (hdr != origUsedShmemSegAddr)
 		elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
 			 hdr, origUsedShmemSegAddr);
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index a4b53b3..7432223 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -454,6 +454,12 @@ static void InitPostmasterDeathWatchHandle(void);
 static pid_t waitpid(pid_t pid, int *exitstatus, int options);
 static void WINAPI pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
 
+/*
+ * During internal_forkexec(), the child signals this auto-reset event to
+ * indicate that it is no longer at risk of needing a fork retry.
+ */
+static HANDLE win32ReAttach;
+
 static HANDLE win32ChildQueue;
 
 typedef struct
@@ -520,6 +526,7 @@ typedef struct
 	int			max_safe_fds;
 	int			MaxBackends;
 #ifdef WIN32
+	HANDLE		win32ReAttach;
 	HANDLE		PostmasterHandle;
 	HANDLE		initial_signal_pipe;
 	HANDLE		syslogPipe[2];
@@ -556,6 +563,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
 #define EXIT_STATUS_0(st)  ((st) == 0)
 #define EXIT_STATUS_1(st)  (WIFEXITED(st) && WEXITSTATUS(st) == 1)
 #define EXIT_STATUS_3(st)  (WIFEXITED(st) && WEXITSTATUS(st) == 3)
+/* status 4 means PGSharedMemoryReAttach() failure; see internal_forkexec() */
 
 #ifndef WIN32
 /*
@@ -1197,6 +1205,20 @@ PostmasterMain(int argc, char *argv[])
 
 #ifdef WIN32
 
+	{
+		SECURITY_ATTRIBUTES sa;
+
+		sa.nLength = sizeof(sa);
+		sa.lpSecurityDescriptor = NULL;
+		sa.bInheritHandle = TRUE;
+
+		win32ReAttach = CreateEvent(&sa, FALSE, FALSE, NULL);
+		if (win32ReAttach == NULL)
+			ereport(FATAL,
+					(errmsg("could not create event to facilitate fork emulation: error code %lu",
+							GetLastError())));
+	}
+
 	/*
 	 * Initialize I/O completion port used to deliver list of dead children.
 	 */
@@ -4524,6 +4546,8 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	BackendParameters *param;
 	SECURITY_ATTRIBUTES sa;
 	char		paramHandleStr[32];
+	HANDLE		wait_handles[2];
+	bool		done;
 	win32_deadchild_waitinfo *childinfo;
 
 	/* Make sure caller set up argv properly */
@@ -4650,8 +4674,7 @@ retry:
 
 	/*
 	 * Now that the backend variables are written out, we start the child
-	 * thread so it can start initializing while we set up the rest of the
-	 * parent state.
+	 * thread so it can start initializing.
 	 */
 	if (ResumeThread(pi.hThread) == -1)
 	{
@@ -4673,6 +4696,64 @@ retry:
 	}
 
 	/*
+	 * Block until child dies or uses SetEvent(win32ReAttach) to indicate that
+	 * it reattached to shared memory.
+	 */
+	wait_handles[0] = win32ReAttach;
+	wait_handles[1] = pi.hProcess;
+	done = false;
+	while (!done)
+	{
+		DWORD		rc,
+					exitcode;
+
+		rc = WaitForMultipleObjectsEx(2, wait_handles, FALSE, INFINITE, TRUE);
+		switch (rc)
+		{
+			case WAIT_OBJECT_0: /* child reattached */
+				done = true;
+				break;
+			case WAIT_OBJECT_0 + 1: /* child exited */
+				done = true;
+				if (GetExitCodeProcess(pi.hProcess, &exitcode) &&
+					exitcode == 4)
+				{
+					/* child already made a log entry */
+					CloseHandle(pi.hProcess);
+					CloseHandle(pi.hThread);
+					if (++retry_count < 100)
+						goto retry;
+					ereport(LOG,
+							(errmsg("giving up after too many tries to reattach to shared memory"),
+							 errhint("This might be caused by ASLR or antivirus software.")));
+					return -1;
+				}
+				/* else, let pgwin32_deadchild_callback() handle the exit */
+				break;
+			case WAIT_IO_COMPLETION:
+
+				/*
+				 * The system interrupted the wait to execute an I/O
+				 * completion routine or asynchronous procedure call in this
+				 * thread.  PostgreSQL does not provoke either of these, but
+				 * atypical loaded DLLs or even other processes might do so.
+				 * Now, resume waiting.
+				 */
+				break;
+			case WAIT_FAILED:
+				ereport(FATAL,
+						(errmsg("could not wait for new child to start: error code %lu",
+								GetLastError())));
+				break;
+			default:
+				elog(FATAL,
+					 "unexpected return code from WaitForMultipleObjectsEx(): %lu",
+					 rc);
+				break;
+		}
+	}
+
+	/*
 	 * Queue a waiter for to signal when this child dies. The wait will be
 	 * handled automatically by an operating system thread pool.
 	 *
@@ -4787,6 +4868,14 @@ SubPostmasterMain(int argc, char *argv[])
 	else
 		PGSharedMemoryNoReAttach();
 
+#ifdef WIN32
+	if (!SetEvent(win32ReAttach))
+		elog(FATAL,
+			 "could not indicate, to postmaster, reattach to shared memory: error code %lu",
+			 GetLastError());
+	/* postmaster is now free to return from internal_forkexec() */
+#endif
+
 	/* autovacuum needs this set before calling InitProcess */
 	if (strcmp(argv[1], "--forkavlauncher") == 0)
 		AutovacuumLauncherIAm();
@@ -6030,6 +6119,7 @@ save_backend_variables(BackendParameters *param, Port *port,
 	param->MaxBackends = MaxBackends;
 
 #ifdef WIN32
+	param->win32ReAttach = win32ReAttach;
 	param->PostmasterHandle = PostmasterHandle;
 	if (!write_duplicated_handle(&param->initial_signal_pipe,
 								 pgwin32_create_signal_listener(childPid),
@@ -6262,6 +6352,7 @@ restore_backend_variables(BackendParameters *param, Port *port)
 	MaxBackends = param->MaxBackends;
 
 #ifdef WIN32
+	win32ReAttach = param->win32ReAttach;
 	PostmasterHandle = param->PostmasterHandle;
 	pgwin32_initial_signal_pipe = param->initial_signal_pipe;
 #else