Skip to content

Commit c1772ad

Browse files
committed
Change the way that LWLocks for extensions are allocated.
The previous RequestAddinLWLocks() method had several disadvantages. First, the locks would be in the main tranche; we've recently decided that it's useful for LWLocks used for separate purposes to have separate tranche IDs. Second, there wasn't any correlation between what code called RequestAddinLWLocks() and what code called LWLockAssign(); when multiple modules are in use, it could become quite difficult to troubleshoot problems where LWLockAssign() ran out of locks. To fix, create a concept of named LWLock tranches which can be used either by extension or by core code. Amit Kapila and Robert Haas
1 parent 5ef244a commit c1772ad

File tree

7 files changed

+210
-50
lines changed

7 files changed

+210
-50
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ _PG_init(void)
404404
* resources in pgss_shmem_startup().
405405
*/
406406
RequestAddinShmemSpace(pgss_memsize());
407-
RequestAddinLWLocks(1);
407+
RequestNamedLWLockTranche("pg_stat_statements", 1);
408408

409409
/*
410410
* Install hooks.
@@ -480,7 +480,7 @@ pgss_shmem_startup(void)
480480
if (!found)
481481
{
482482
/* First time through ... */
483-
pgss->lock = LWLockAssign();
483+
pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
484484
pgss->cur_median_usage = ASSUMED_MEDIAN_INIT;
485485
pgss->mean_query_len = ASSUMED_LENGTH_INIT;
486486
SpinLockInit(&pgss->mutex);

doc/src/sgml/xfunc.sgml

+6-3
Original file line numberDiff line numberDiff line change
@@ -3335,9 +3335,12 @@ void RequestAddinShmemSpace(int size)
33353335
<para>
33363336
LWLocks are reserved by calling:
33373337
<programlisting>
3338-
void RequestAddinLWLocks(int n)
3338+
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
33393339
</programlisting>
3340-
from <function>_PG_init</>.
3340+
from <function>_PG_init</>. This will ensure that an array of
3341+
<literal>num_lwlocks</> LWLocks is available under the name
3342+
<literal>tranche_name</>. Use <function>GetNamedLWLockTranche</>
3343+
to get a pointer to this array.
33413344
</para>
33423345
<para>
33433346
To avoid possible race-conditions, each backend should use the LWLock
@@ -3356,7 +3359,7 @@ if (!ptr)
33563359
{
33573360
initialize contents of shmem area;
33583361
acquire any requested LWLocks using:
3359-
ptr->mylockid = LWLockAssign();
3362+
ptr->locks = GetNamedLWLockTranche("my tranche name");
33603363
}
33613364
LWLockRelease(AddinShmemInitLock);
33623365
}

src/backend/postmaster/postmaster.c

+6
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,8 @@ typedef struct
485485
#ifndef HAVE_SPINLOCKS
486486
PGSemaphore SpinlockSemaArray;
487487
#endif
488+
int NamedLWLockTrancheRequests;
489+
NamedLWLockTranche *NamedLWLockTrancheArray;
488490
LWLockPadded *MainLWLockArray;
489491
slock_t *ProcStructLock;
490492
PROC_HDR *ProcGlobal;
@@ -5800,6 +5802,8 @@ save_backend_variables(BackendParameters *param, Port *port,
58005802
#ifndef HAVE_SPINLOCKS
58015803
param->SpinlockSemaArray = SpinlockSemaArray;
58025804
#endif
5805+
param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
5806+
param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
58035807
param->MainLWLockArray = MainLWLockArray;
58045808
param->ProcStructLock = ProcStructLock;
58055809
param->ProcGlobal = ProcGlobal;
@@ -6031,6 +6035,8 @@ restore_backend_variables(BackendParameters *param, Port *port)
60316035
#ifndef HAVE_SPINLOCKS
60326036
SpinlockSemaArray = param->SpinlockSemaArray;
60336037
#endif
6038+
NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
6039+
NamedLWLockTrancheArray = param->NamedLWLockTrancheArray;
60346040
MainLWLockArray = param->MainLWLockArray;
60356041
ProcStructLock = param->ProcStructLock;
60366042
ProcGlobal = param->ProcGlobal;

src/backend/storage/lmgr/lwlock.c

+177-35
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,20 @@ typedef struct LWLockHandle
144144
static int num_held_lwlocks = 0;
145145
static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS];
146146

147-
static int lock_addin_request = 0;
148-
static bool lock_addin_request_allowed = true;
147+
/* struct representing the LWLock tranche request for named tranche */
148+
typedef struct NamedLWLockTrancheRequest
149+
{
150+
char tranche_name[NAMEDATALEN];
151+
int num_lwlocks;
152+
} NamedLWLockTrancheRequest;
153+
154+
NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
155+
static int NamedLWLockTrancheRequestsAllocated = 0;
156+
int NamedLWLockTrancheRequests = 0;
157+
158+
NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
159+
160+
static bool lock_named_request_allowed = true;
149161

150162
#ifdef LWLOCK_STATS
151163
typedef struct lwlock_stats_key
@@ -335,6 +347,22 @@ get_lwlock_stats_entry(LWLock *lock)
335347
#endif /* LWLOCK_STATS */
336348

337349

350+
/*
351+
* Compute number of LWLocks required by named tranches. These will be
352+
* allocated in the main array.
353+
*/
354+
static int
355+
NumLWLocksByNamedTranches(void)
356+
{
357+
int numLocks = 0;
358+
int i;
359+
360+
for (i = 0; i < NamedLWLockTrancheRequests; i++)
361+
numLocks += NamedLWLockTrancheRequestArray[i].num_lwlocks;
362+
363+
return numLocks;
364+
}
365+
338366
/*
339367
* Compute number of LWLocks to allocate in the main array.
340368
*/
@@ -353,64 +381,49 @@ NumLWLocks(void)
353381
/* Predefined LWLocks */
354382
numLocks = NUM_FIXED_LWLOCKS;
355383

356-
/*
357-
* Add any requested by loadable modules; for backwards-compatibility
358-
* reasons, allocate at least NUM_USER_DEFINED_LWLOCKS of them even if
359-
* there are no explicit requests.
360-
*/
361-
lock_addin_request_allowed = false;
362-
numLocks += Max(lock_addin_request, NUM_USER_DEFINED_LWLOCKS);
384+
/* Disallow named LWLocks' requests after startup */
385+
lock_named_request_allowed = false;
363386

364387
return numLocks;
365388
}
366389

367-
368-
/*
369-
* RequestAddinLWLocks
370-
* Request that extra LWLocks be allocated for use by
371-
* a loadable module.
372-
*
373-
* This is only useful if called from the _PG_init hook of a library that
374-
* is loaded into the postmaster via shared_preload_libraries. Once
375-
* shared memory has been allocated, calls will be ignored. (We could
376-
* raise an error, but it seems better to make it a no-op, so that
377-
* libraries containing such calls can be reloaded if needed.)
378-
*/
379-
void
380-
RequestAddinLWLocks(int n)
381-
{
382-
if (IsUnderPostmaster || !lock_addin_request_allowed)
383-
return; /* too late */
384-
lock_addin_request += n;
385-
}
386-
387-
388390
/*
389-
* Compute shmem space needed for LWLocks.
391+
* Compute shmem space needed for LWLocks and named tranches.
390392
*/
391393
Size
392394
LWLockShmemSize(void)
393395
{
394396
Size size;
397+
int i;
395398
int numLocks = NumLWLocks();
396399

400+
numLocks += NumLWLocksByNamedTranches();
401+
397402
/* Space for the LWLock array. */
398403
size = mul_size(numLocks, sizeof(LWLockPadded));
399404

400405
/* Space for dynamic allocation counter, plus room for alignment. */
401406
size = add_size(size, 3 * sizeof(int) + LWLOCK_PADDED_SIZE);
402407

408+
/* space for named tranches. */
409+
size = add_size(size, mul_size(NamedLWLockTrancheRequests, sizeof(NamedLWLockTranche)));
410+
411+
/* space for name of each tranche. */
412+
for (i = 0; i < NamedLWLockTrancheRequests; i++)
413+
size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);
414+
403415
return size;
404416
}
405417

406-
407418
/*
408-
* Allocate shmem space for the main LWLock array and initialize it. We also
409-
* register the main tranch here.
419+
* Allocate shmem space for the main LWLock array and named tranches and
420+
* initialize it. We also register the main and named tranche here.
410421
*/
411422
void
412423
CreateLWLocks(void)
413424
{
425+
int i;
426+
414427
StaticAssertExpr(LW_VAL_EXCLUSIVE > (uint32) MAX_BACKENDS,
415428
"MAX_BACKENDS too big for lwlock.c");
416429

@@ -421,11 +434,13 @@ CreateLWLocks(void)
421434
if (!IsUnderPostmaster)
422435
{
423436
int numLocks = NumLWLocks();
437+
int numNamedLocks = NumLWLocksByNamedTranches();
424438
Size spaceLocks = LWLockShmemSize();
425439
LWLockPadded *lock;
426440
int *LWLockCounter;
427441
char *ptr;
428442
int id;
443+
int j;
429444

430445
/* Allocate space */
431446
ptr = (char *) ShmemAlloc(spaceLocks);
@@ -438,7 +453,7 @@ CreateLWLocks(void)
438453

439454
MainLWLockArray = (LWLockPadded *) ptr;
440455

441-
/* Initialize all LWLocks in main array */
456+
/* Initialize all fixed LWLocks in main array */
442457
for (id = 0, lock = MainLWLockArray; id < numLocks; id++, lock++)
443458
LWLockInitialize(&lock->lock, LWTRANCHE_MAIN);
444459

@@ -453,6 +468,40 @@ CreateLWLocks(void)
453468
LWLockCounter[0] = NUM_FIXED_LWLOCKS;
454469
LWLockCounter[1] = numLocks;
455470
LWLockCounter[2] = LWTRANCHE_FIRST_USER_DEFINED;
471+
472+
/* Initialize named tranches. */
473+
if (NamedLWLockTrancheRequests > 0)
474+
{
475+
char *trancheNames;
476+
477+
NamedLWLockTrancheArray = (NamedLWLockTranche *)
478+
&MainLWLockArray[numLocks + numNamedLocks];
479+
480+
trancheNames = (char *) NamedLWLockTrancheArray +
481+
(NamedLWLockTrancheRequests * sizeof(NamedLWLockTranche));
482+
lock = &MainLWLockArray[numLocks];
483+
484+
for (i = 0; i < NamedLWLockTrancheRequests; i++)
485+
{
486+
NamedLWLockTrancheRequest *request;
487+
NamedLWLockTranche *tranche;
488+
char *name;
489+
490+
request = &NamedLWLockTrancheRequestArray[i];
491+
tranche = &NamedLWLockTrancheArray[i];
492+
493+
name = trancheNames;
494+
trancheNames += strlen(request->tranche_name) + 1;
495+
strcpy(name, request->tranche_name);
496+
tranche->lwLockTranche.name = name;
497+
tranche->trancheId = LWLockNewTrancheId();
498+
tranche->lwLockTranche.array_base = lock;
499+
tranche->lwLockTranche.array_stride = sizeof(LWLockPadded);
500+
501+
for (j = 0; j < request->num_lwlocks; j++, lock++)
502+
LWLockInitialize(&lock->lock, tranche->trancheId);
503+
}
504+
}
456505
}
457506

458507
if (LWLockTrancheArray == NULL)
@@ -468,6 +517,11 @@ CreateLWLocks(void)
468517
MainLWLockTranche.array_base = MainLWLockArray;
469518
MainLWLockTranche.array_stride = sizeof(LWLockPadded);
470519
LWLockRegisterTranche(LWTRANCHE_MAIN, &MainLWLockTranche);
520+
521+
/* Register named tranches. */
522+
for (i = 0; i < NamedLWLockTrancheRequests; i++)
523+
LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
524+
&NamedLWLockTrancheArray[i].lwLockTranche);
471525
}
472526

473527
/*
@@ -507,6 +561,45 @@ LWLockAssign(void)
507561
return result;
508562
}
509563

564+
/*
565+
* GetNamedLWLockTranche - returns the base address of LWLock from the
566+
* specified tranche.
567+
*
568+
* Caller needs to retrieve the requested number of LWLocks starting from
569+
* the base lock address returned by this API. This can be used for
570+
* tranches that are requested by using RequestNamedLWLockTranche() API.
571+
*/
572+
LWLockPadded *
573+
GetNamedLWLockTranche(const char *tranche_name)
574+
{
575+
int lock_pos;
576+
int i;
577+
int *LWLockCounter;
578+
579+
LWLockCounter = (int *) ((char *) MainLWLockArray - 3 * sizeof(int));
580+
581+
/*
582+
* Obtain the position of base address of LWLock belonging to requested
583+
* tranche_name in MainLWLockArray. LWLocks for named tranches are placed
584+
* in MainLWLockArray after LWLocks specified by LWLockCounter[1].
585+
*/
586+
lock_pos = LWLockCounter[1];
587+
for (i = 0; i < NamedLWLockTrancheRequests; i++)
588+
{
589+
if (strcmp(NamedLWLockTrancheRequestArray[i].tranche_name,
590+
tranche_name) == 0)
591+
return &MainLWLockArray[lock_pos];
592+
593+
lock_pos += NamedLWLockTrancheRequestArray[i].num_lwlocks;
594+
}
595+
596+
if (i >= NamedLWLockTrancheRequests)
597+
elog(ERROR, "requested tranche is not registered");
598+
599+
/* just to keep compiler quiet */
600+
return NULL;
601+
}
602+
510603
/*
511604
* Allocate a new tranche ID.
512605
*/
@@ -551,6 +644,55 @@ LWLockRegisterTranche(int tranche_id, LWLockTranche *tranche)
551644
LWLockTrancheArray[tranche_id] = tranche;
552645
}
553646

647+
/*
648+
* RequestNamedLWLockTranche
649+
* Request that extra LWLocks be allocated during postmaster
650+
* startup.
651+
*
652+
* This is only useful for extensions if called from the _PG_init hook
653+
* of a library that is loaded into the postmaster via
654+
* shared_preload_libraries. Once shared memory has been allocated, calls
655+
* will be ignored. (We could raise an error, but it seems better to make
656+
* it a no-op, so that libraries containing such calls can be reloaded if
657+
* needed.)
658+
*/
659+
void
660+
RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
661+
{
662+
NamedLWLockTrancheRequest *request;
663+
664+
if (IsUnderPostmaster || !lock_named_request_allowed)
665+
return; /* too late */
666+
667+
if (NamedLWLockTrancheRequestArray == NULL)
668+
{
669+
NamedLWLockTrancheRequestsAllocated = 16;
670+
NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
671+
MemoryContextAlloc(TopMemoryContext,
672+
NamedLWLockTrancheRequestsAllocated
673+
* sizeof(NamedLWLockTrancheRequest));
674+
}
675+
676+
if (NamedLWLockTrancheRequests >= NamedLWLockTrancheRequestsAllocated)
677+
{
678+
int i = NamedLWLockTrancheRequestsAllocated;
679+
680+
while (i <= NamedLWLockTrancheRequests)
681+
i *= 2;
682+
683+
NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
684+
repalloc(NamedLWLockTrancheRequestArray,
685+
i * sizeof(NamedLWLockTrancheRequest));
686+
NamedLWLockTrancheRequestsAllocated = i;
687+
}
688+
689+
request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests];
690+
Assert(strlen(tranche_name) + 1 < NAMEDATALEN);
691+
StrNCpy(request->tranche_name, tranche_name, NAMEDATALEN);
692+
request->num_lwlocks = num_lwlocks;
693+
NamedLWLockTrancheRequests++;
694+
}
695+
554696
/*
555697
* LWLockInitialize - initialize a new lwlock; it's initially unlocked
556698
*/

src/include/pg_config_manual.h

-5
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@
5151
#define SEQ_MAXVALUE PG_INT64_MAX
5252
#define SEQ_MINVALUE (-SEQ_MAXVALUE)
5353

54-
/*
55-
* Number of spare LWLocks to allocate for user-defined add-on code.
56-
*/
57-
#define NUM_USER_DEFINED_LWLOCKS 4
58-
5954
/*
6055
* When we don't have native spinlocks, we use semaphores to simulate them.
6156
* Decreasing this value reduces consumption of OS resources; increasing it

0 commit comments

Comments
 (0)