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 ;
@@ -133,6 +155,12 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
133
155
(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
134
156
errmsg ("huge pages not supported on this platform" )));
135
157
158
+ ShmemProtectiveRegion = VirtualAlloc (NULL ,PROTECTIVE_REGION_SIZE ,
159
+ MEM_RESERVE ,PAGE_NOACCESS );
160
+ if (ShmemProtectiveRegion == NULL )
161
+ elog (FATAL ,"could not reserve memory region: error code %lu" ,
162
+ GetLastError ());
163
+
136
164
/* Room for a header? */
137
165
Assert (size > MAXALIGN (sizeof (PGShmemHeader )));
138
166
@@ -263,22 +291,26 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
263
291
* an already existing shared memory segment, using the handle inherited from
264
292
* the postmaster.
265
293
*
266
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
267
- * routine. The caller must have already restored them to the postmaster's
268
- * values.
294
+ *ShmemProtectiveRegion, UsedShmemSegID and UsedShmemSegAddr are implicit
295
+ *parameters to this routine. The caller must have already restored them to
296
+ *the postmaster's values.
269
297
*/
270
298
void
271
299
PGSharedMemoryReAttach (void )
272
300
{
273
301
PGShmemHeader * hdr ;
274
302
void * origUsedShmemSegAddr = UsedShmemSegAddr ;
275
303
304
+ Assert (ShmemProtectiveRegion != NULL );
276
305
Assert (UsedShmemSegAddr != NULL );
277
306
Assert (IsUnderPostmaster );
278
307
279
308
/*
280
- * Release memory regionreservation that was made by the postmaster
309
+ * Release memory regionreservations made by the postmaster
281
310
*/
311
+ if (VirtualFree (ShmemProtectiveRegion ,0 ,MEM_RELEASE )== 0 )
312
+ elog (FATAL ,"failed to release reserved memory region (addr=%p): error code %lu" ,
313
+ ShmemProtectiveRegion ,GetLastError ());
282
314
if (VirtualFree (UsedShmemSegAddr ,0 ,MEM_RELEASE )== 0 )
283
315
elog (FATAL ,"failed to release reserved memory region (addr=%p): error code %lu" ,
284
316
UsedShmemSegAddr ,GetLastError ());
@@ -307,13 +339,14 @@ PGSharedMemoryReAttach(void)
307
339
* The child process startup logic might or might not call PGSharedMemoryDetach
308
340
* after this; make sure that it will be a no-op if called.
309
341
*
310
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
311
- * routine. The caller must have already restored them to the postmaster's
312
- * values.
342
+ *ShmemProtectiveRegion, UsedShmemSegID and UsedShmemSegAddr are implicit
343
+ *parameters to this routine. The caller must have already restored them to
344
+ *the postmaster's values.
313
345
*/
314
346
void
315
347
PGSharedMemoryNoReAttach (void )
316
348
{
349
+ Assert (ShmemProtectiveRegion != NULL );
317
350
Assert (UsedShmemSegAddr != NULL );
318
351
Assert (IsUnderPostmaster );
319
352
@@ -340,12 +373,25 @@ PGSharedMemoryNoReAttach(void)
340
373
* Rather, this is for subprocesses that have inherited an attachment and want
341
374
* to get rid of it.
342
375
*
343
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
344
- * routine.
376
+ *ShmemProtectiveRegion, UsedShmemSegID and UsedShmemSegAddr are implicit
377
+ *parameters to this routine.
345
378
*/
346
379
void
347
380
PGSharedMemoryDetach (void )
348
381
{
382
+ /*
383
+ * Releasing the protective region liberates an unimportant quantity of
384
+ * address space, but be tidy.
385
+ */
386
+ if (ShmemProtectiveRegion != NULL )
387
+ {
388
+ if (VirtualFree (ShmemProtectiveRegion ,0 ,MEM_RELEASE )== 0 )
389
+ elog (LOG ,"failed to release reserved memory region (addr=%p): error code %lu" ,
390
+ ShmemProtectiveRegion ,GetLastError ());
391
+
392
+ ShmemProtectiveRegion = NULL ;
393
+ }
394
+
349
395
/* Unmap the view, if it's mapped */
350
396
if (UsedShmemSegAddr != NULL )
351
397
{
@@ -403,29 +449,47 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
403
449
{
404
450
void * address ;
405
451
452
+ Assert (ShmemProtectiveRegion != NULL );
406
453
Assert (UsedShmemSegAddr != NULL );
407
454
Assert (UsedShmemSegSize != 0 );
408
455
409
- address = VirtualAllocEx (hChild ,UsedShmemSegAddr ,UsedShmemSegSize ,
410
- MEM_RESERVE ,PAGE_READWRITE );
456
+ /* ShmemProtectiveRegion */
457
+ address = VirtualAllocEx (hChild ,ShmemProtectiveRegion ,
458
+ PROTECTIVE_REGION_SIZE ,
459
+ MEM_RESERVE ,PAGE_NOACCESS );
411
460
if (address == NULL )
412
461
{
413
462
/* Don't use FATAL since we're running in the postmaster */
414
463
elog (LOG ,"could not reserve shared memory region (addr=%p) for child %p: error code %lu" ,
415
- UsedShmemSegAddr ,hChild ,GetLastError ());
464
+ ShmemProtectiveRegion ,hChild ,GetLastError ());
416
465
return false;
417
466
}
418
- if (address != UsedShmemSegAddr )
467
+ if (address != ShmemProtectiveRegion )
419
468
{
420
469
/*
421
470
* Should never happen - in theory if allocation granularity causes
422
471
* strange effects it could, so check just in case.
423
472
*
424
473
* Don't use FATAL since we're running in the postmaster.
425
474
*/
475
+ elog (LOG ,"reserved shared memory region got incorrect address %p, expected %p" ,
476
+ address ,ShmemProtectiveRegion );
477
+ return false;
478
+ }
479
+
480
+ /* UsedShmemSegAddr */
481
+ address = VirtualAllocEx (hChild ,UsedShmemSegAddr ,UsedShmemSegSize ,
482
+ MEM_RESERVE ,PAGE_READWRITE );
483
+ if (address == NULL )
484
+ {
485
+ elog (LOG ,"could not reserve shared memory region (addr=%p) for child %p: error code %lu" ,
486
+ UsedShmemSegAddr ,hChild ,GetLastError ());
487
+ return false;
488
+ }
489
+ if (address != UsedShmemSegAddr )
490
+ {
426
491
elog (LOG ,"reserved shared memory region got incorrect address %p, expected %p" ,
427
492
address ,UsedShmemSegAddr );
428
- VirtualFreeEx (hChild ,address ,0 ,MEM_RELEASE );
429
493
return false;
430
494
}
431
495