]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Backpatch reservation of shared memory region during backend startup on
authorMagnus Hagander <magnus@hagander.net>
Fri, 23 Jul 2010 13:53:30 +0000 (13:53 +0000)
committerMagnus Hagander <magnus@hagander.net>
Fri, 23 Jul 2010 13:53:30 +0000 (13:53 +0000)
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
src/backend/port/win32/shmem.c
src/backend/postmaster/postmaster.c
src/include/port/win32.h

index 04d8e6f09f9f0390bb2e1f8fa1a446b453cb2d2c..956883579a4987b8b4e748a62bc9ce99a9c8719f 100644 (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;
 }
index c7865bda325e12fa7ca1d3383b04e64cd3d2e902..2ba144c9a4a9867e20dfbaaa8f5c977ea9c2d65f 100644 (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;
+}
index 69f37c02ad7e0d4f087d5da5b816f51f17d4a2b5..583604fcdf2f597681070f4111db80f169d02a25 100644 (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
index 514efcdb2d8f13c8e5a85ecd8649352914badaaf..50c711e121f9357d84e9efd63dc37448530375df 100644 (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);