From 90e15467b456b1008e3ebe4e4146aca6d063acc4 Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Fri, 23 Jul 2010 13:53:30 +0000 Subject: [PATCH] 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. --- src/backend/port/sysv_shmem.c | 8 +++- src/backend/port/win32/shmem.c | 62 ++++++++++++++++++++++++++++- src/backend/postmaster/postmaster.c | 23 ++++++++++- src/include/port/win32.h | 5 ++- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 04d8e6f09f..956883579a 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -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; } diff --git a/src/backend/port/win32/shmem.c b/src/backend/port/win32/shmem.c index c7865bda32..2ba144c9a4 100644 --- a/src/backend/port/win32/shmem.c +++ b/src/backend/port/win32/shmem.c @@ -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; +} diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 69f37c02ad..583604fcdf 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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 diff --git a/src/include/port/win32.h b/src/include/port/win32.h index 514efcdb2d..50c711e121 100644 --- a/src/include/port/win32.h +++ b/src/include/port/win32.h @@ -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);