w32attach-retry-block-v1.patch
text/plain
Filename: w32attach-retry-block-v1.patch
Type: text/plain
Part: 0
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(¶m->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