17
17
#include "storage/ipc.h"
18
18
#include "storage/pg_shmem.h"
19
19
20
+ /*
21
+ * Early in a process's life, Windows asynchronously creates threads for the
22
+ * process's "default thread pool"
23
+ * (https://docs.microsoft.com/en-us/windows/desktop/ProcThread/thread-pools).
24
+ * Occasionally, thread creation allocates a stack after
25
+ * PGSharedMemoryReAttach() has released UsedShmemSegAddr and before it has
26
+ * mapped shared memory at UsedShmemSegAddr. This would cause mapping to fail
27
+ * if the allocator preferred the just-released region for allocating the new
28
+ * thread stack. We observed such failures in some Windows Server 2016
29
+ * configurations. To give the system another region to prefer, reserve and
30
+ * release an additional, protective region immediately before reserving or
31
+ * releasing shared memory. The idea is that, if the allocator handed out
32
+ * REGION1 pages before REGION2 pages at one occasion, it will do so whenever
33
+ * both regions are free. Windows Server 2016 exhibits that behavior, and a
34
+ * system behaving differently would have less need to protect
35
+ * UsedShmemSegAddr. The protective region must be at least large enough for
36
+ * one thread stack. However, ten times as much is less than 2% of the 32-bit
37
+ * address space and is negligible relative to the 64-bit address space.
38
+ */
39
+ #define PROTECTIVE_REGION_SIZE (10 * WIN32_STACK_RLIMIT)
40
+ void * ShmemProtectiveRegion = NULL ;
41
+
20
42
HANDLE UsedShmemSegID = INVALID_HANDLE_VALUE ;
21
43
void * UsedShmemSegAddr = NULL ;
22
44
static Size UsedShmemSegSize = 0 ;
@@ -192,6 +214,12 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
192
214
Size orig_size = size ;
193
215
DWORD flProtect = PAGE_READWRITE ;
194
216
217
+ ShmemProtectiveRegion = VirtualAlloc (NULL , PROTECTIVE_REGION_SIZE ,
218
+ MEM_RESERVE , PAGE_NOACCESS );
219
+ if (ShmemProtectiveRegion == NULL )
220
+ elog (FATAL , "could not reserve memory region: error code %lu" ,
221
+ GetLastError ());
222
+
195
223
/* Room for a header? */
196
224
Assert (size > MAXALIGN (sizeof (PGShmemHeader )));
197
225
@@ -370,22 +398,26 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
370
398
* an already existing shared memory segment, using the handle inherited from
371
399
* the postmaster.
372
400
*
373
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
374
- * routine. The caller must have already restored them to the postmaster's
375
- * values.
401
+ * ShmemProtectiveRegion, UsedShmemSegID and UsedShmemSegAddr are implicit
402
+ * parameters to this routine. The caller must have already restored them to
403
+ * the postmaster's values.
376
404
*/
377
405
void
378
406
PGSharedMemoryReAttach (void )
379
407
{
380
408
PGShmemHeader * hdr ;
381
409
void * origUsedShmemSegAddr = UsedShmemSegAddr ;
382
410
411
+ Assert (ShmemProtectiveRegion != NULL );
383
412
Assert (UsedShmemSegAddr != NULL );
384
413
Assert (IsUnderPostmaster );
385
414
386
415
/*
387
- * Release memory region reservation that was made by the postmaster
416
+ * Release memory region reservations made by the postmaster
388
417
*/
418
+ if (VirtualFree (ShmemProtectiveRegion , 0 , MEM_RELEASE ) == 0 )
419
+ elog (FATAL , "failed to release reserved memory region (addr=%p): error code %lu" ,
420
+ ShmemProtectiveRegion , GetLastError ());
389
421
if (VirtualFree (UsedShmemSegAddr , 0 , MEM_RELEASE ) == 0 )
390
422
elog (FATAL , "failed to release reserved memory region (addr=%p): error code %lu" ,
391
423
UsedShmemSegAddr , GetLastError ());
@@ -414,13 +446,14 @@ PGSharedMemoryReAttach(void)
414
446
* The child process startup logic might or might not call PGSharedMemoryDetach
415
447
* after this; make sure that it will be a no-op if called.
416
448
*
417
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
418
- * routine. The caller must have already restored them to the postmaster's
419
- * values.
449
+ * ShmemProtectiveRegion, UsedShmemSegID and UsedShmemSegAddr are implicit
450
+ * parameters to this routine. The caller must have already restored them to
451
+ * the postmaster's values.
420
452
*/
421
453
void
422
454
PGSharedMemoryNoReAttach (void )
423
455
{
456
+ Assert (ShmemProtectiveRegion != NULL );
424
457
Assert (UsedShmemSegAddr != NULL );
425
458
Assert (IsUnderPostmaster );
426
459
@@ -447,12 +480,25 @@ PGSharedMemoryNoReAttach(void)
447
480
* Rather, this is for subprocesses that have inherited an attachment and want
448
481
* to get rid of it.
449
482
*
450
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
451
- * routine.
483
+ * ShmemProtectiveRegion, UsedShmemSegID and UsedShmemSegAddr are implicit
484
+ * parameters to this routine.
452
485
*/
453
486
void
454
487
PGSharedMemoryDetach (void )
455
488
{
489
+ /*
490
+ * Releasing the protective region liberates an unimportant quantity of
491
+ * address space, but be tidy.
492
+ */
493
+ if (ShmemProtectiveRegion != NULL )
494
+ {
495
+ if (VirtualFree (ShmemProtectiveRegion , 0 , MEM_RELEASE ) == 0 )
496
+ elog (LOG , "failed to release reserved memory region (addr=%p): error code %lu" ,
497
+ ShmemProtectiveRegion , GetLastError ());
498
+
499
+ ShmemProtectiveRegion = NULL ;
500
+ }
501
+
456
502
/* Unmap the view, if it's mapped */
457
503
if (UsedShmemSegAddr != NULL )
458
504
{
@@ -510,29 +556,47 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
510
556
{
511
557
void * address ;
512
558
559
+ Assert (ShmemProtectiveRegion != NULL );
513
560
Assert (UsedShmemSegAddr != NULL );
514
561
Assert (UsedShmemSegSize != 0 );
515
562
516
- address = VirtualAllocEx (hChild , UsedShmemSegAddr , UsedShmemSegSize ,
517
- MEM_RESERVE , PAGE_READWRITE );
563
+ /* ShmemProtectiveRegion */
564
+ address = VirtualAllocEx (hChild , ShmemProtectiveRegion ,
565
+ PROTECTIVE_REGION_SIZE ,
566
+ MEM_RESERVE , PAGE_NOACCESS );
518
567
if (address == NULL )
519
568
{
520
569
/* Don't use FATAL since we're running in the postmaster */
521
570
elog (LOG , "could not reserve shared memory region (addr=%p) for child %p: error code %lu" ,
522
- UsedShmemSegAddr , hChild , GetLastError ());
571
+ ShmemProtectiveRegion , hChild , GetLastError ());
523
572
return false;
524
573
}
525
- if (address != UsedShmemSegAddr )
574
+ if (address != ShmemProtectiveRegion )
526
575
{
527
576
/*
528
577
* Should never happen - in theory if allocation granularity causes
529
578
* strange effects it could, so check just in case.
530
579
*
531
580
* Don't use FATAL since we're running in the postmaster.
532
581
*/
582
+ elog (LOG , "reserved shared memory region got incorrect address %p, expected %p" ,
583
+ address , ShmemProtectiveRegion );
584
+ return false;
585
+ }
586
+
587
+ /* UsedShmemSegAddr */
588
+ address = VirtualAllocEx (hChild , UsedShmemSegAddr , UsedShmemSegSize ,
589
+ MEM_RESERVE , PAGE_READWRITE );
590
+ if (address == NULL )
591
+ {
592
+ elog (LOG , "could not reserve shared memory region (addr=%p) for child %p: error code %lu" ,
593
+ UsedShmemSegAddr , hChild , GetLastError ());
594
+ return false;
595
+ }
596
+ if (address != UsedShmemSegAddr )
597
+ {
533
598
elog (LOG , "reserved shared memory region got incorrect address %p, expected %p" ,
534
599
address , UsedShmemSegAddr );
535
- VirtualFreeEx (hChild , address , 0 , MEM_RELEASE );
536
600
return false;
537
601
}
538
602
0 commit comments