Reserve the shared memory region during backend startup on Windows, so

that memory allocated by starting third party DLLs doesn't end up
conflicting with it.

Hopefully this solves the long-time issue with "could not reattach
to shared memory" errors on Win32.

Patch from Tsutomu Yamada and me, based on idea from Trevor Talbot.
This commit is contained in:
Magnus Hagander 2009-07-24 20:12:42 +00:00
parent 5e22994127
commit a7e587863c
3 changed files with 85 additions and 4 deletions

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.11 2009/06/11 14:49:00 momjian Exp $
* $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.12 2009/07/24 20:12:42 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,6 +18,7 @@
unsigned long UsedShmemSegID = 0;
void *UsedShmemSegAddr = NULL;
static Size UsedShmemSegSize = 0;
static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
@ -233,6 +234,7 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
/* Save info for possible future use */
UsedShmemSegAddr = memAddress;
UsedShmemSegSize = size;
UsedShmemSegID = (unsigned long) hmap2;
return hdr;
@ -257,6 +259,13 @@ PGSharedMemoryReAttach(void)
Assert(UsedShmemSegAddr != NULL);
Assert(IsUnderPostmaster);
/*
* Release memory region reservation that was made by the postmaster
*/
if (VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0)
elog(FATAL, "failed to release reserved memory region (addr=%p): %lu",
UsedShmemSegAddr, GetLastError());
hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
if (!hdr)
elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu",
@ -302,3 +311,53 @@ pgwin32_SharedMemoryDelete(int status, Datum shmId)
if (!CloseHandle((HANDLE) DatumGetInt32(shmId)))
elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
}
/*
* pgwin32_ReserveSharedMemoryRegion(hChild)
*
* Reserve the memory region that will be used for shared memory in a child
* process. It is called before the child process starts, to make sure the
* memory is available.
*
* Once the child starts, DLLs loading in different order or threads getting
* scheduled differently may allocate memory which can conflict with the
* address space we need for our shared memory. By reserving the shared
* memory region before the child starts, and freeing it only just before we
* attempt to get access to the shared memory forces these allocations to
* be given different address ranges that don't conflict.
*
* NOTE! This function executes in the postmaster, and should for this
* reason not use elog(FATAL) since that would take down the postmaster.
*/
int
pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
{
void *address;
Assert(UsedShmemSegAddr != NULL);
Assert(UsedShmemSegSize != 0);
address = VirtualAllocEx(hChild, UsedShmemSegAddr, UsedShmemSegSize,
MEM_RESERVE, PAGE_READWRITE);
if (address == NULL) {
/* Don't use FATAL since we're running in the postmaster */
elog(LOG, "could not reserve shared memory region (addr=%p) for child %lu: %lu",
UsedShmemSegAddr, hChild, GetLastError());
return false;
}
if (address != UsedShmemSegAddr)
{
/*
* Should never happen - in theory if allocation granularity causes strange
* effects it could, so check just in case.
*
* Don't use FATAL since we're running in the postmaster.
*/
elog(LOG, "reserved shared memory region got incorrect address %p, expected %p",
address, UsedShmemSegAddr);
VirtualFreeEx(hChild, address, 0, MEM_RELEASE);
return false;
}
return true;
}

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.584 2009/07/08 18:55:35 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.585 2009/07/24 20:12:42 mha Exp $
*
* NOTES
*
@ -3635,7 +3635,7 @@ internal_forkexec(int argc, char *argv[], Port *port)
return -1; /* log made by save_backend_variables */
}
/* Drop the shared memory that is now inherited to the backend */
/* Drop the parameter shared memory that is now inherited to the backend */
if (!UnmapViewOfFile(param))
elog(LOG, "could not unmap view of backend parameter file: error code %d",
(int) GetLastError());
@ -3643,6 +3643,25 @@ internal_forkexec(int argc, char *argv[], Port *port)
elog(LOG, "could not close handle to backend parameter file: error code %d",
(int) GetLastError());
/*
* Reserve the memory region used by our main shared memory segment before we
* resume the child process.
*/
if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
{
/*
* Failed to reserve the memory, so terminate the newly created
* process and give up.
*/
if (!TerminateProcess(pi.hProcess, 255))
ereport(ERROR,
(errmsg_internal("could not terminate process that failed to reserve memory: error code %d",
(int) GetLastError())));
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return -1; /* logging done made by pgwin32_ReserveSharedMemoryRegion() */
}
/*
* 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

View File

@ -1,4 +1,4 @@
/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.88 2009/06/11 14:49:12 momjian Exp $ */
/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.89 2009/07/24 20:12:42 mha Exp $ */
#if defined(_MSC_VER) || defined(__BORLANDC__)
#define WIN32_ONLY_COMPILER
@ -288,6 +288,9 @@ extern int pgwin32_is_admin(void);
extern int pgwin32_is_service(void);
#endif
/* in backend/port/win32_shmem.c */
extern int pgwin32_ReserveSharedMemoryRegion(HANDLE);
/* in port/win32error.c */
extern void _dosmaperr(unsigned long);