Commit a7e58786 authored by Magnus Hagander's avatar Magnus Hagander

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.
parent 5e229941
......@@ -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;
}
......@@ -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
......
/* $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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment