Backpatch reservation of shared memory region during backend startup on

Windows, so that memory allocated by starting third party DLLs doesn't end
up conflicting.

The same functionality has been in 8.3 and 8.4 for almost a year, and seems
to have solved some of the more common shared memory errors on Windows.
This commit is contained in:
Magnus Hagander 2010-07-23 13:53:30 +00:00
parent ae1b65b5aa
commit 90e15467b4
4 changed files with 93 additions and 5 deletions

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.47.2.2 2010/05/01 22:46:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.47.2.3 2010/07/23 13:53:29 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -49,6 +49,9 @@ typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
unsigned long UsedShmemSegID = 0;
void *UsedShmemSegAddr = NULL;
#ifdef WIN32
Size UsedShmemSegSize = 0;
#endif
static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
static void IpcMemoryDetach(int status, Datum shmaddr);
@ -446,6 +449,9 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
/* Save info for possible future use */
UsedShmemSegAddr = memAddress;
UsedShmemSegID = (unsigned long) NextShmemSegID;
#ifdef WIN32
UsedShmemSegSize = size;
#endif
return hdr;
}

View File

@ -6,14 +6,17 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/win32/shmem.c,v 1.13.2.1 2009/05/04 08:36:42 mha Exp $
* $PostgreSQL: pgsql/src/backend/port/win32/shmem.c,v 1.13.2.2 2010/07/23 13:53:30 mha Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
static DWORD s_segsize = 0;
extern void *UsedShmemSegAddr;
extern Size UsedShmemSegSize;
/* Detach from a shared mem area based on its address */
int
@ -29,6 +32,13 @@ shmdt(const void *shmaddr)
void *
shmat(int memId, void *shmaddr, int flag)
{
/* Release the memory region reserved in the postmaster */
if (IsUnderPostmaster)
{
if (VirtualFree(shmaddr, 0, MEM_RELEASE) == 0)
elog(FATAL, "failed to release reserved memory region (addr=%p): %lu",
shmaddr, GetLastError());
}
/* TODO -- shmat needs to count # attached to shared mem */
void *lpmem = MapViewOfFileEx((HANDLE) memId,
FILE_MAP_WRITE | FILE_MAP_READ,
@ -128,3 +138,53 @@ shmget(int memKey, int size, int flag)
return (int) hmap;
}
/*
* 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.505.2.8 2010/04/01 20:12:42 heikki Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.505.2.9 2010/07/23 13:53:30 mha Exp $
*
* NOTES
*
@ -3186,7 +3186,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());
@ -3194,6 +3194,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.63.2.3 2007/11/29 16:44:26 mha Exp $ */
/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.63.2.4 2010/07/23 13:53:30 mha Exp $ */
#if defined(_MSC_VER) || defined(__BORLANDC__)
#define WIN32_ONLY_COMPILER
@ -263,6 +263,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);