Skip to content

Commit a7e5878

Browse files
committed
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.
1 parent 5e22994 commit a7e5878

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

src/backend/port/win32_shmem.c

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
77
*
88
* IDENTIFICATION
9-
* $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.11 2009/06/11 14:49:00 momjian Exp $
9+
* $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.12 2009/07/24 20:12:42 mha Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -18,6 +18,7 @@
1818

1919
unsigned long UsedShmemSegID = 0;
2020
void *UsedShmemSegAddr = NULL;
21+
static Size UsedShmemSegSize = 0;
2122

2223
static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
2324

@@ -233,6 +234,7 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
233234

234235
/* Save info for possible future use */
235236
UsedShmemSegAddr = memAddress;
237+
UsedShmemSegSize = size;
236238
UsedShmemSegID = (unsigned long) hmap2;
237239

238240
return hdr;
@@ -257,6 +259,13 @@ PGSharedMemoryReAttach(void)
257259
Assert(UsedShmemSegAddr != NULL);
258260
Assert(IsUnderPostmaster);
259261

262+
/*
263+
* Release memory region reservation that was made by the postmaster
264+
*/
265+
if (VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0)
266+
elog(FATAL, "failed to release reserved memory region (addr=%p): %lu",
267+
UsedShmemSegAddr, GetLastError());
268+
260269
hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
261270
if (!hdr)
262271
elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu",
@@ -302,3 +311,53 @@ pgwin32_SharedMemoryDelete(int status, Datum shmId)
302311
if (!CloseHandle((HANDLE) DatumGetInt32(shmId)))
303312
elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
304313
}
314+
315+
/*
316+
* pgwin32_ReserveSharedMemoryRegion(hChild)
317+
*
318+
* Reserve the memory region that will be used for shared memory in a child
319+
* process. It is called before the child process starts, to make sure the
320+
* memory is available.
321+
*
322+
* Once the child starts, DLLs loading in different order or threads getting
323+
* scheduled differently may allocate memory which can conflict with the
324+
* address space we need for our shared memory. By reserving the shared
325+
* memory region before the child starts, and freeing it only just before we
326+
* attempt to get access to the shared memory forces these allocations to
327+
* be given different address ranges that don't conflict.
328+
*
329+
* NOTE! This function executes in the postmaster, and should for this
330+
* reason not use elog(FATAL) since that would take down the postmaster.
331+
*/
332+
int
333+
pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
334+
{
335+
void *address;
336+
337+
Assert(UsedShmemSegAddr != NULL);
338+
Assert(UsedShmemSegSize != 0);
339+
340+
address = VirtualAllocEx(hChild, UsedShmemSegAddr, UsedShmemSegSize,
341+
MEM_RESERVE, PAGE_READWRITE);
342+
if (address == NULL) {
343+
/* Don't use FATAL since we're running in the postmaster */
344+
elog(LOG, "could not reserve shared memory region (addr=%p) for child %lu: %lu",
345+
UsedShmemSegAddr, hChild, GetLastError());
346+
return false;
347+
}
348+
if (address != UsedShmemSegAddr)
349+
{
350+
/*
351+
* Should never happen - in theory if allocation granularity causes strange
352+
* effects it could, so check just in case.
353+
*
354+
* Don't use FATAL since we're running in the postmaster.
355+
*/
356+
elog(LOG, "reserved shared memory region got incorrect address %p, expected %p",
357+
address, UsedShmemSegAddr);
358+
VirtualFreeEx(hChild, address, 0, MEM_RELEASE);
359+
return false;
360+
}
361+
362+
return true;
363+
}

src/backend/postmaster/postmaster.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
*
3838
*
3939
* IDENTIFICATION
40-
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.584 2009/07/08 18:55:35 tgl Exp $
40+
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.585 2009/07/24 20:12:42 mha Exp $
4141
*
4242
* NOTES
4343
*
@@ -3635,14 +3635,33 @@ internal_forkexec(int argc, char *argv[], Port *port)
36353635
return -1; /* log made by save_backend_variables */
36363636
}
36373637

3638-
/* Drop the shared memory that is now inherited to the backend */
3638+
/* Drop the parameter shared memory that is now inherited to the backend */
36393639
if (!UnmapViewOfFile(param))
36403640
elog(LOG, "could not unmap view of backend parameter file: error code %d",
36413641
(int) GetLastError());
36423642
if (!CloseHandle(paramHandle))
36433643
elog(LOG, "could not close handle to backend parameter file: error code %d",
36443644
(int) GetLastError());
36453645

3646+
/*
3647+
* Reserve the memory region used by our main shared memory segment before we
3648+
* resume the child process.
3649+
*/
3650+
if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
3651+
{
3652+
/*
3653+
* Failed to reserve the memory, so terminate the newly created
3654+
* process and give up.
3655+
*/
3656+
if (!TerminateProcess(pi.hProcess, 255))
3657+
ereport(ERROR,
3658+
(errmsg_internal("could not terminate process that failed to reserve memory: error code %d",
3659+
(int) GetLastError())));
3660+
CloseHandle(pi.hProcess);
3661+
CloseHandle(pi.hThread);
3662+
return -1; /* logging done made by pgwin32_ReserveSharedMemoryRegion() */
3663+
}
3664+
36463665
/*
36473666
* Now that the backend variables are written out, we start the child
36483667
* thread so it can start initializing while we set up the rest of the

src/include/port/win32.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.88 2009/06/11 14:49:12 momjian Exp $ */
1+
/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.89 2009/07/24 20:12:42 mha Exp $ */
22

33
#if defined(_MSC_VER) || defined(__BORLANDC__)
44
#define WIN32_ONLY_COMPILER
@@ -288,6 +288,9 @@ extern int pgwin32_is_admin(void);
288288
extern int pgwin32_is_service(void);
289289
#endif
290290

291+
/* in backend/port/win32_shmem.c */
292+
extern int pgwin32_ReserveSharedMemoryRegion(HANDLE);
293+
291294
/* in port/win32error.c */
292295
extern void _dosmaperr(unsigned long);
293296

0 commit comments

Comments
 (0)