Skip to content

Commit bc6b03b

Browse files
committed
On Windows, ensure shared memory handle gets closed if not being used.
Postmaster child processes that aren't supposed to be attached to shared memory were not bothering to close the shared memory mapping handle they inherit from the postmaster process. That's mostly harmless, since the handle vanishes anyway when the child process exits -- but the syslogger process, if used, doesn't get killed and restarted during recovery from a backend crash. That meant that Windows doesn't see the shared memory mapping as becoming free, so it doesn't delete it and the postmaster is unable to create a new one, resulting in failure to recover from crashes whenever logging_collector is turned on. Per report from Dmitry Vasilyev. It's a bit astonishing that we'd not figured this out long ago, since it's been broken from the very beginnings of out native Windows support; probably some previously-unexplained trouble reports trace to this. A secondary problem is that on Cygwin (perhaps only in older versions?), exec() may not detach from the shared memory segment after all, in which case these child processes did remain attached to shared memory, posing the risk of an unexpected shared memory clobber if they went off the rails somehow. That may be a long-gone bug, but we can deal with it now if it's still live, by detaching within the infrastructure introduced here to deal with closing the handle. Back-patch to all supported branches. Tom Lane and Amit Kapila
1 parent dfe572d commit bc6b03b

File tree

4 files changed

+116
-25
lines changed

4 files changed

+116
-25
lines changed

src/backend/port/sysv_shmem.c

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
217217

218218
/****************************************************************************/
219219
/* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
220-
/* from process' address spaceq */
220+
/* from process' address space */
221221
/* (called as an on_shmem_exit callback, hence funny argument list) */
222222
/****************************************************************************/
223223
static void
@@ -537,9 +537,10 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
537537
/*
538538
* PGSharedMemoryReAttach
539539
*
540-
* Re-attach to an already existing shared memory segment. In the non
541-
* EXEC_BACKEND case this is not used, because postmaster children inherit
542-
* the shared memory segment attachment via fork().
540+
* This is called during startup of a postmaster child process to re-attach to
541+
* an already existing shared memory segment. This is needed only in the
542+
* EXEC_BACKEND case; otherwise postmaster children inherit the shared memory
543+
* segment attachment via fork().
543544
*
544545
* UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
545546
* routine. The caller must have already restored them to the postmaster's
@@ -572,16 +573,52 @@ PGSharedMemoryReAttach(void)
572573

573574
UsedShmemSegAddr = hdr; /* probably redundant */
574575
}
576+
577+
/*
578+
* PGSharedMemoryNoReAttach
579+
*
580+
* This is called during startup of a postmaster child process when we choose
581+
* *not* to re-attach to the existing shared memory segment. We must clean up
582+
* to leave things in the appropriate state. This is not used in the non
583+
* EXEC_BACKEND case, either.
584+
*
585+
* The child process startup logic might or might not call PGSharedMemoryDetach
586+
* after this; make sure that it will be a no-op if called.
587+
*
588+
* UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
589+
* routine. The caller must have already restored them to the postmaster's
590+
* values.
591+
*/
592+
void
593+
PGSharedMemoryNoReAttach(void)
594+
{
595+
Assert(UsedShmemSegAddr != NULL);
596+
Assert(IsUnderPostmaster);
597+
598+
#ifdef __CYGWIN__
599+
/* cygipc (currently) appears to not detach on exec. */
600+
PGSharedMemoryDetach();
601+
#endif
602+
603+
/* For cleanliness, reset UsedShmemSegAddr to show we're not attached. */
604+
UsedShmemSegAddr = NULL;
605+
/* And the same for UsedShmemSegID. */
606+
UsedShmemSegID = 0;
607+
}
608+
575609
#endif /* EXEC_BACKEND */
576610

577611
/*
578612
* PGSharedMemoryDetach
579613
*
580614
* Detach from the shared memory segment, if still attached. This is not
581-
* intended for use by the process that originally created the segment
582-
* (it will have an on_shmem_exit callback registered to do that). Rather,
583-
* this is for subprocesses that have inherited an attachment and want to
584-
* get rid of it.
615+
* intended to be called explicitly by the process that originally created the
616+
* segment (it will have an on_shmem_exit callback registered to do that).
617+
* Rather, this is for subprocesses that have inherited an attachment and want
618+
* to get rid of it.
619+
*
620+
* UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
621+
* routine.
585622
*/
586623
void
587624
PGSharedMemoryDetach(void)

src/backend/port/win32_shmem.c

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "storage/ipc.h"
1717
#include "storage/pg_shmem.h"
1818

19-
HANDLE UsedShmemSegID = 0;
19+
HANDLE UsedShmemSegID = INVALID_HANDLE_VALUE;
2020
void *UsedShmemSegAddr = NULL;
2121
static Size UsedShmemSegSize = 0;
2222

@@ -82,7 +82,6 @@ GetSharedMemName(void)
8282
* we only care about shmem segments that are associated with the intended
8383
* DataDir. This is an important consideration since accidental matches of
8484
* shmem segment IDs are reasonably common.
85-
*
8685
*/
8786
bool
8887
PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
@@ -114,7 +113,6 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
114113
* or recycle any existing segment. On win32, we always create a new segment,
115114
* since there is no need for recycling (segments go away automatically
116115
* when the last backend exits)
117-
*
118116
*/
119117
PGShmemHeader *
120118
PGSharedMemoryCreate(Size size, bool makePrivate, int port)
@@ -211,9 +209,6 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
211209
elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
212210

213211

214-
/* Register on-exit routine to delete the new segment */
215-
on_shmem_exit(pgwin32_SharedMemoryDelete, PointerGetDatum(hmap2));
216-
217212
/*
218213
* Get a pointer to the new shared memory segment. Map the whole segment
219214
* at once, and let the system decide on the initial address.
@@ -246,14 +241,18 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
246241
UsedShmemSegSize = size;
247242
UsedShmemSegID = hmap2;
248243

244+
/* Register on-exit routine to delete the new segment */
245+
on_shmem_exit(pgwin32_SharedMemoryDelete, PointerGetDatum(hmap2));
246+
249247
return hdr;
250248
}
251249

252250
/*
253251
* PGSharedMemoryReAttach
254252
*
255-
* Re-attach to an already existing shared memory segment. Use the
256-
* handle inherited from the postmaster.
253+
* This is called during startup of a postmaster child process to re-attach to
254+
* an already existing shared memory segment, using the handle inherited from
255+
* the postmaster.
257256
*
258257
* UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
259258
* routine. The caller must have already restored them to the postmaster's
@@ -288,37 +287,88 @@ PGSharedMemoryReAttach(void)
288287
UsedShmemSegAddr = hdr; /* probably redundant */
289288
}
290289

290+
/*
291+
* PGSharedMemoryNoReAttach
292+
*
293+
* This is called during startup of a postmaster child process when we choose
294+
* *not* to re-attach to the existing shared memory segment. We must clean up
295+
* to leave things in the appropriate state.
296+
*
297+
* The child process startup logic might or might not call PGSharedMemoryDetach
298+
* after this; make sure that it will be a no-op if called.
299+
*
300+
* UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
301+
* routine. The caller must have already restored them to the postmaster's
302+
* values.
303+
*/
304+
void
305+
PGSharedMemoryNoReAttach(void)
306+
{
307+
Assert(UsedShmemSegAddr != NULL);
308+
Assert(IsUnderPostmaster);
309+
310+
/*
311+
* Under Windows we will not have mapped the segment, so we don't need to
312+
* un-map it. Just reset UsedShmemSegAddr to show we're not attached.
313+
*/
314+
UsedShmemSegAddr = NULL;
315+
316+
/*
317+
* We *must* close the inherited shmem segment handle, else Windows will
318+
* consider the existence of this process to mean it can't release the
319+
* shmem segment yet. We can now use PGSharedMemoryDetach to do that.
320+
*/
321+
PGSharedMemoryDetach();
322+
}
323+
291324
/*
292325
* PGSharedMemoryDetach
293326
*
294327
* Detach from the shared memory segment, if still attached. This is not
295-
* intended for use by the process that originally created the segment. Rather,
296-
* this is for subprocesses that have inherited an attachment and want to
297-
* get rid of it.
328+
* intended to be called explicitly by the process that originally created the
329+
* segment (it will have an on_shmem_exit callback registered to do that).
330+
* Rather, this is for subprocesses that have inherited an attachment and want
331+
* to get rid of it.
332+
*
333+
* UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
334+
* routine.
298335
*/
299336
void
300337
PGSharedMemoryDetach(void)
301338
{
339+
/* Unmap the view, if it's mapped */
302340
if (UsedShmemSegAddr != NULL)
303341
{
304342
if (!UnmapViewOfFile(UsedShmemSegAddr))
305-
elog(LOG, "could not unmap view of shared memory: error code %lu", GetLastError());
343+
elog(LOG, "could not unmap view of shared memory: error code %lu",
344+
GetLastError());
306345

307346
UsedShmemSegAddr = NULL;
308347
}
348+
349+
/* And close the shmem handle, if we have one */
350+
if (UsedShmemSegID != INVALID_HANDLE_VALUE)
351+
{
352+
if (!CloseHandle(UsedShmemSegID))
353+
elog(LOG, "could not close handle to shared memory: error code %lu",
354+
GetLastError());
355+
356+
UsedShmemSegID = INVALID_HANDLE_VALUE;
357+
}
309358
}
310359

311360

312361
/*
313-
* pgwin32_SharedMemoryDelete(status, shmId) deletes a shared memory segment
314-
* (called as an on_shmem_exit callback, hence funny argument list)
362+
* pgwin32_SharedMemoryDelete
363+
*
364+
* Detach from and delete the shared memory segment
365+
* (called as an on_shmem_exit callback, hence funny argument list)
315366
*/
316367
static void
317368
pgwin32_SharedMemoryDelete(int status, Datum shmId)
318369
{
370+
Assert(DatumGetPointer(shmId) == UsedShmemSegID);
319371
PGSharedMemoryDetach();
320-
if (!CloseHandle(DatumGetPointer(shmId)))
321-
elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
322372
}
323373

324374
/*

src/backend/postmaster/postmaster.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4585,14 +4585,17 @@ SubPostmasterMain(int argc, char *argv[])
45854585
/*
45864586
* If appropriate, physically re-attach to shared memory segment. We want
45874587
* to do this before going any further to ensure that we can attach at the
4588-
* same address the postmaster used.
4588+
* same address the postmaster used. On the other hand, if we choose not
4589+
* to re-attach, we may have other cleanup to do.
45894590
*/
45904591
if (strcmp(argv[1], "--forkbackend") == 0 ||
45914592
strcmp(argv[1], "--forkavlauncher") == 0 ||
45924593
strcmp(argv[1], "--forkavworker") == 0 ||
45934594
strcmp(argv[1], "--forkboot") == 0 ||
45944595
strncmp(argv[1], "--forkbgworker=", 15) == 0)
45954596
PGSharedMemoryReAttach();
4597+
else
4598+
PGSharedMemoryNoReAttach();
45964599

45974600
/* autovacuum needs this set before calling InitProcess */
45984601
if (strcmp(argv[1], "--forkavlauncher") == 0)

src/include/storage/pg_shmem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ extern HANDLE UsedShmemSegID;
4848
extern void *UsedShmemSegAddr;
4949

5050
extern void PGSharedMemoryReAttach(void);
51+
extern void PGSharedMemoryNoReAttach(void);
5152
#endif
5253

5354
extern PGShmemHeader *PGSharedMemoryCreate(Size size, bool makePrivate,

0 commit comments

Comments
 (0)