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