pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feedClean up NamedLWLockTranche stuff
18+ messages / 4 participants
[nested] [flat]
Commits from this thread:
2026-03-30 bd365b1ae5 Fix outdated comment on MainLWLockArray
2026-03-27 2407c8db15 Fix RequestNamedLWLockTranche in single-user mode
2026-03-27 98993150c0 Minor comment fixes to yesterday's LWLock tranche refactoring
2026-03-26 06d859aaf4 Move ShmemIndexLock into ShmemAllocator
2026-03-26 30d432502b Use ShmemInitStruct to allocate lwlock.c's shared memory
2026-03-26 12e3e0f2c8 Use a separate spinlock to protect LWLockTranches
2026-03-26 d6eba30a24 Refactor how user-defined LWLock tranches are stored in shmem
2026-03-26 cc88481aeb Rename MAX_NAMED_TRANCHES to MAX_USER_DEFINED_TRANCHES
* Clean up NamedLWLockTranche stuff
@ 2026-03-26 12:16 Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-26 12:16 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
While working on the new shmem allocation functions, I looked at how the
NamedLWLockTranche stuff works now in lwlock.c, and I have to say it's a
bit of a mess.
What is a "named tranche"? It usually means tranches requested with
RequestNamedLWLockTranche() at postmaster startup. But all tranches have
a name. LWLockTrancheNames includes all user-defined tranches, also ones
assigned with LWLockNewTrancheId(), and MAX_NAMED_TRANCHES is the
maximum for all of them.
At postmaster startup, NamedLWLockTrancheRequests points to a
backend-private array. But after startup, and always in backends, it
points to a copy in shared memory and
LocalNamedLWLockTrancheRequestArray is used to hold the original. It
took me a while to realize that NamedLWLockTrancheRequests in shared
memory is *not* updated when you call LWLockNewTrancheId(), it only
holds the requests made with RequestNamedLWLockTranche() before startup.
I propose the attached refactorings to make this less confusing. See
commit messages for details.
- Heikki
Attachments: download all (5 files, .tar.gz)
v1-0001-Rename-MAX_NAMED_TRANCHES-to-MAX_USER_DEFINED_TRA.patch (text/x-patch, 3.0K) download
From 245c4c5086e138f3e65b33b542282fa8563fd0ea Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 26 Mar 2026 13:26:49 +0200
Subject: [PATCH v1 1/5] Rename MAX_NAMED_TRANCHES to MAX_USER_DEFINED_TRANCHES
The "named tranches" term is a little confusing. In most places it
refers specifically to tranches reqeusted with
RequestNamedLWLockTranche(), event though all built-in tranches and
tranches allocated with LWLockNewTrancheId() also have a name. But
MAX_NAMED_TRANCHES is the maximum for all tranches requested with
either RequestNamedLWLockTranche() or LWLockNewTrancheId(). The "user
defined" term was already used in LWTRANCHE_FIRST_USER_DEFINED, so
let's standardize on that to mean tranches allocated with either
RequestNamedLWLockTranche() or LWLockNewTrancheId().
---
src/backend/storage/lmgr/lwlock.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 49382de88fc..ee482aee647 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -202,7 +202,7 @@ int *LWLockCounter = NULL;
/* backend-local counter of registered tranches */
static int LocalLWLockCounter;
-#define MAX_NAMED_TRANCHES 256
+#define MAX_USER_DEFINED_TRANCHES 256
static void InitializeLWLocks(void);
static inline void LWLockReportWaitStart(LWLock *lock);
@@ -415,9 +415,9 @@ LWLockShmemSize(void)
/* Space for dynamic allocation counter. */
size = MAXALIGN(sizeof(int));
- /* Space for named tranches. */
- size = add_size(size, mul_size(MAX_NAMED_TRANCHES, sizeof(char *)));
- size = add_size(size, mul_size(MAX_NAMED_TRANCHES, NAMEDATALEN));
+ /* Space for user-defined tranches. */
+ size = add_size(size, mul_size(MAX_USER_DEFINED_TRANCHES, sizeof(char *)));
+ size = add_size(size, mul_size(MAX_USER_DEFINED_TRANCHES, NAMEDATALEN));
/*
* Make space for named tranche requests. This is done for the benefit of
@@ -454,10 +454,10 @@ CreateLWLocks(void)
*LWLockCounter = LWTRANCHE_FIRST_USER_DEFINED;
ptr += MAXALIGN(sizeof(int));
- /* Initialize tranche names */
+ /* Initialize user-defined tranche names */
LWLockTrancheNames = (char **) ptr;
- ptr += MAX_NAMED_TRANCHES * sizeof(char *);
- for (int i = 0; i < MAX_NAMED_TRANCHES; i++)
+ ptr += MAX_USER_DEFINED_TRANCHES * sizeof(char *);
+ for (int i = 0; i < MAX_USER_DEFINED_TRANCHES; i++)
{
LWLockTrancheNames[i] = ptr;
ptr += NAMEDATALEN;
@@ -616,13 +616,13 @@ LWLockNewTrancheId(const char *name)
*/
SpinLockAcquire(ShmemLock);
- if (*LWLockCounter - LWTRANCHE_FIRST_USER_DEFINED >= MAX_NAMED_TRANCHES)
+ if (*LWLockCounter - LWTRANCHE_FIRST_USER_DEFINED >= MAX_USER_DEFINED_TRANCHES)
{
SpinLockRelease(ShmemLock);
ereport(ERROR,
(errmsg("maximum number of tranches already registered"),
errdetail("No more than %d tranches may be registered.",
- MAX_NAMED_TRANCHES)));
+ MAX_USER_DEFINED_TRANCHES)));
}
result = (*LWLockCounter)++;
--
2.47.3
v1-0002-Refactor-how-user-defined-LWLock-tranches-are-sto.patch (text/x-patch, 21.1K) download
From c175e2d64ed15811ea4fe8c22a421e31224f49b6 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 26 Mar 2026 13:26:54 +0200
Subject: [PATCH v1 2/5] Refactor how user-defined LWLock tranches are stored
in shmem
Merge the LWLockTranches and NamedLWLockTrancheRequest data structures
in shared memory into one array of user-defined tranches. The
NamedLWLockTrancheRequest list is now only used in postmaster, to hold
the requests until shared memory has been initialized.
Introduce a C struct, LWLockTranches, to hold all the different fields
kept in shared memory. This gives an easier overview of what are all
the things kept in shared memory. Previously, we had separate pointers
for LWLockTrancheNames, LWLockCounter and the (shared memory copy of)
NamedLWLockTrancheRequestArray.
---
src/backend/postmaster/launch_backend.c | 15 +-
src/backend/storage/lmgr/lwlock.c | 327 +++++++++++-------------
src/include/storage/lwlock.h | 7 +-
src/tools/pgindent/typedefs.list | 1 +
4 files changed, 150 insertions(+), 200 deletions(-)
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 30357845729..8c134eaca88 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -99,10 +99,7 @@ typedef struct
#ifdef USE_INJECTION_POINTS
struct InjectionPointsCtl *ActiveInjectionPoints;
#endif
- int NamedLWLockTrancheRequests;
- NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray;
- char **LWLockTrancheNames;
- int *LWLockCounter;
+ LWLockTrancheShmemData *LWLockTranches;
LWLockPadded *MainLWLockArray;
PROC_HDR *ProcGlobal;
PGPROC *AuxiliaryProcs;
@@ -729,10 +726,7 @@ save_backend_variables(BackendParameters *param,
param->ActiveInjectionPoints = ActiveInjectionPoints;
#endif
- param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
- param->NamedLWLockTrancheRequestArray = NamedLWLockTrancheRequestArray;
- param->LWLockTrancheNames = LWLockTrancheNames;
- param->LWLockCounter = LWLockCounter;
+ param->LWLockTranches = LWLockTranches;
param->MainLWLockArray = MainLWLockArray;
param->ProcGlobal = ProcGlobal;
param->AuxiliaryProcs = AuxiliaryProcs;
@@ -988,10 +982,7 @@ restore_backend_variables(BackendParameters *param)
ActiveInjectionPoints = param->ActiveInjectionPoints;
#endif
- NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
- NamedLWLockTrancheRequestArray = param->NamedLWLockTrancheRequestArray;
- LWLockTrancheNames = param->LWLockTrancheNames;
- LWLockCounter = param->LWLockCounter;
+ LWLockTranches = param->LWLockTranches;
MainLWLockArray = param->MainLWLockArray;
ProcGlobal = param->ProcGlobal;
AuxiliaryProcs = param->AuxiliaryProcs;
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index ee482aee647..2e3f2f2a6ff 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -127,8 +127,8 @@ StaticAssertDecl((LW_VAL_EXCLUSIVE & LW_FLAG_MASK) == 0,
* in lwlocklist.h. We absorb the names of these tranches, too.
*
* 3. Extensions can create new tranches, via either RequestNamedLWLockTranche
- * or LWLockNewTrancheId. These names are stored in shared memory and can be
- * accessed via LWLockTrancheNames.
+ * or LWLockNewTrancheId. These are stored in shared memory and can be
+ * accessed via LWLockTranches.
*
* All these names are user-visible as wait event names, so choose with care
* ... and do not forget to update the documentation's list of wait events.
@@ -145,15 +145,6 @@ StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
LWTRANCHE_FIRST_USER_DEFINED,
"missing entries in BuiltinTrancheNames[]");
-/*
- * This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED, and
- * points to the shared memory locations of the names of all
- * dynamically-created tranches. Backends inherit the pointer by fork from the
- * postmaster (except in the EXEC_BACKEND case, where we have special measures
- * to pass it down).
- */
-char **LWLockTrancheNames = NULL;
-
/*
* This points to the main array of LWLocks in shared memory. Backends inherit
* the pointer by fork from the postmaster (except in the EXEC_BACKEND case,
@@ -178,33 +169,49 @@ typedef struct LWLockHandle
static int num_held_lwlocks = 0;
static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS];
-/* struct representing the LWLock tranche request for named tranche */
-typedef struct NamedLWLockTrancheRequest
-{
- char tranche_name[NAMEDATALEN];
- int num_lwlocks;
-} NamedLWLockTrancheRequest;
+/* Maximum number of LWLock tranches that can be assigned by extensions */
+#define MAX_USER_DEFINED_TRANCHES 256
/*
- * NamedLWLockTrancheRequests is the valid length of the request array. These
- * variables are non-static so that launch_backend.c can copy them to child
- * processes in EXEC_BACKEND builds.
+ * Shared memory structure holding user-defined tranches.
*/
-int NamedLWLockTrancheRequests = 0;
-NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
+typedef struct LWLockTrancheShmemData
+{
+ /* This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED */
+ struct
+ {
+ char name[NAMEDATALEN];
-/* postmaster's local copy of the request array */
-static NamedLWLockTrancheRequest *LocalNamedLWLockTrancheRequestArray = NULL;
+ /*
+ * Index of the tranche's locks in MainLWLockArray if this tranche was
+ * allocated with RequestNamedLWLockTranche(), or -1 if the tranche
+ * was allocated with LWLockNewTrancheId()
+ */
+ int main_array_idx;
+ } user_defined[MAX_USER_DEFINED_TRANCHES];
-/* shared memory counter of registered tranches */
-int *LWLockCounter = NULL;
+ int num_user_defined; /* 'user_defined' entries in use */
+} LWLockTrancheShmemData;
-/* backend-local counter of registered tranches */
-static int LocalLWLockCounter;
+LWLockTrancheShmemData *LWLockTranches;
-#define MAX_USER_DEFINED_TRANCHES 256
+/* backend-local copy of NamedLWLockTranches->num_user_defined */
+static int LocalNumUserDefinedTranches;
+
+/*
+ * NamedLWLockTrancheRequests is a list of tranches requested with
+ * RequestNamedLWLockTranche(). It is only valid in the postmaster; after
+ * startup the tranches are tracked in LWLockTranches in shared memory.
+ */
+typedef struct NamedLWLockTrancheRequest
+{
+ char tranche_name[NAMEDATALEN];
+ int num_lwlocks;
+} NamedLWLockTrancheRequest;
+
+static List *NamedLWLockTrancheRequests = NIL;
-static void InitializeLWLocks(void);
+static void InitializeLWLocks(int numLocks);
static inline void LWLockReportWaitStart(LWLock *lock);
static inline void LWLockReportWaitEnd(void);
static const char *GetLWTrancheName(uint16 trancheId);
@@ -383,10 +390,14 @@ static int
NumLWLocksForNamedTranches(void)
{
int numLocks = 0;
- int i;
+ ListCell *lc;
- for (i = 0; i < NamedLWLockTrancheRequests; i++)
- numLocks += NamedLWLockTrancheRequestArray[i].num_lwlocks;
+ foreach(lc, NamedLWLockTrancheRequests)
+ {
+ NamedLWLockTrancheRequest *request = (NamedLWLockTrancheRequest *) lfirst(lc);
+
+ numLocks += request->num_lwlocks;
+ }
return numLocks;
}
@@ -398,37 +409,13 @@ Size
LWLockShmemSize(void)
{
Size size;
- int numLocks = NUM_FIXED_LWLOCKS;
-
- /*
- * If re-initializing shared memory, the request array will no longer be
- * accessible, so switch to the copy in postmaster's local memory. We'll
- * copy it back into shared memory later when CreateLWLocks() is called
- * again.
- */
- if (LocalNamedLWLockTrancheRequestArray)
- NamedLWLockTrancheRequestArray = LocalNamedLWLockTrancheRequestArray;
-
- /* Calculate total number of locks needed in the main array. */
- numLocks += NumLWLocksForNamedTranches();
-
- /* Space for dynamic allocation counter. */
- size = MAXALIGN(sizeof(int));
+ int numLocks;
- /* Space for user-defined tranches. */
- size = add_size(size, mul_size(MAX_USER_DEFINED_TRANCHES, sizeof(char *)));
- size = add_size(size, mul_size(MAX_USER_DEFINED_TRANCHES, NAMEDATALEN));
+ /* Space for user-defined tranches */
+ size = sizeof(LWLockTrancheShmemData);
- /*
- * Make space for named tranche requests. This is done for the benefit of
- * EXEC_BACKEND builds, which otherwise wouldn't be able to call
- * GetNamedLWLockTranche() outside postmaster.
- */
- size = add_size(size, mul_size(NamedLWLockTrancheRequests,
- sizeof(NamedLWLockTrancheRequest)));
-
- /* Space for the LWLock array, plus room for cache line alignment. */
- size = add_size(size, LWLOCK_PADDED_SIZE);
+ /* Space for the LWLock array */
+ numLocks = NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches();
size = add_size(size, mul_size(numLocks, sizeof(LWLockPadded)));
return size;
@@ -441,54 +428,21 @@ LWLockShmemSize(void)
void
CreateLWLocks(void)
{
+ int numLocks;
+
if (!IsUnderPostmaster)
{
- Size spaceLocks = LWLockShmemSize();
- char *ptr;
-
- /* Allocate space */
- ptr = (char *) ShmemAlloc(spaceLocks);
+ /* Allocate space for LWLockTranches */
+ LWLockTranches = (LWLockTrancheShmemData *)
+ ShmemAlloc(sizeof(LWLockTrancheShmemData));
/* Initialize the dynamic-allocation counter for tranches */
- LWLockCounter = (int *) ptr;
- *LWLockCounter = LWTRANCHE_FIRST_USER_DEFINED;
- ptr += MAXALIGN(sizeof(int));
-
- /* Initialize user-defined tranche names */
- LWLockTrancheNames = (char **) ptr;
- ptr += MAX_USER_DEFINED_TRANCHES * sizeof(char *);
- for (int i = 0; i < MAX_USER_DEFINED_TRANCHES; i++)
- {
- LWLockTrancheNames[i] = ptr;
- ptr += NAMEDATALEN;
- }
+ LWLockTranches->num_user_defined = 0;
- /*
- * Move named tranche requests to shared memory. This is done for the
- * benefit of EXEC_BACKEND builds, which otherwise wouldn't be able to
- * call GetNamedLWLockTranche() outside postmaster.
- */
- if (NamedLWLockTrancheRequests > 0)
- {
- /*
- * Save the pointer to the request array in postmaster's local
- * memory. We'll need it if we ever need to re-initialize shared
- * memory after a crash.
- */
- LocalNamedLWLockTrancheRequestArray = NamedLWLockTrancheRequestArray;
-
- memcpy(ptr, NamedLWLockTrancheRequestArray,
- NamedLWLockTrancheRequests * sizeof(NamedLWLockTrancheRequest));
- NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) ptr;
- ptr += NamedLWLockTrancheRequests * sizeof(NamedLWLockTrancheRequest);
- }
-
- /* Ensure desired alignment of LWLock array */
- ptr += LWLOCK_PADDED_SIZE - ((uintptr_t) ptr) % LWLOCK_PADDED_SIZE;
- MainLWLockArray = (LWLockPadded *) ptr;
-
- /* Initialize all LWLocks */
- InitializeLWLocks();
+ /* Allocate and initialize the main array */
+ numLocks = NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches();
+ MainLWLockArray = (LWLockPadded *) ShmemAlloc(numLocks * sizeof(LWLockPadded));
+ InitializeLWLocks(numLocks);
}
}
@@ -496,52 +450,52 @@ CreateLWLocks(void)
* Initialize LWLocks that are fixed and those belonging to named tranches.
*/
static void
-InitializeLWLocks(void)
+InitializeLWLocks(int numLocks)
{
- int id;
- int i;
- int j;
- LWLockPadded *lock;
+ int pos = 0;
+ ListCell *lc;
/* Initialize all individual LWLocks in main array */
- for (id = 0, lock = MainLWLockArray; id < NUM_INDIVIDUAL_LWLOCKS; id++, lock++)
- LWLockInitialize(&lock->lock, id);
+ for (int id = 0; id < NUM_INDIVIDUAL_LWLOCKS; id++)
+ LWLockInitialize(&MainLWLockArray[pos++].lock, id);
/* Initialize buffer mapping LWLocks in main array */
- lock = MainLWLockArray + BUFFER_MAPPING_LWLOCK_OFFSET;
- for (id = 0; id < NUM_BUFFER_PARTITIONS; id++, lock++)
- LWLockInitialize(&lock->lock, LWTRANCHE_BUFFER_MAPPING);
+ Assert(pos == BUFFER_MAPPING_LWLOCK_OFFSET);
+ for (int i = 0; i < NUM_BUFFER_PARTITIONS; i++)
+ LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_BUFFER_MAPPING);
/* Initialize lmgrs' LWLocks in main array */
- lock = MainLWLockArray + LOCK_MANAGER_LWLOCK_OFFSET;
- for (id = 0; id < NUM_LOCK_PARTITIONS; id++, lock++)
- LWLockInitialize(&lock->lock, LWTRANCHE_LOCK_MANAGER);
+ Assert(pos == LOCK_MANAGER_LWLOCK_OFFSET);
+ for (int i = 0; i < NUM_LOCK_PARTITIONS; i++)
+ LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_LOCK_MANAGER);
/* Initialize predicate lmgrs' LWLocks in main array */
- lock = MainLWLockArray + PREDICATELOCK_MANAGER_LWLOCK_OFFSET;
- for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++)
- LWLockInitialize(&lock->lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);
+ Assert(pos == PREDICATELOCK_MANAGER_LWLOCK_OFFSET);
+ for (int i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++)
+ LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);
/*
* Copy the info about any named tranches into shared memory (so that
* other processes can see it), and initialize the requested LWLocks.
*/
- if (NamedLWLockTrancheRequests > 0)
+ Assert(pos == NUM_FIXED_LWLOCKS);
+ foreach(lc, NamedLWLockTrancheRequests)
{
- lock = &MainLWLockArray[NUM_FIXED_LWLOCKS];
-
- for (i = 0; i < NamedLWLockTrancheRequests; i++)
- {
- NamedLWLockTrancheRequest *request;
- int tranche;
+ NamedLWLockTrancheRequest *request = (NamedLWLockTrancheRequest *) lfirst(lc);
+ int idx = (LWLockTranches->num_user_defined++);
- request = &NamedLWLockTrancheRequestArray[i];
- tranche = LWLockNewTrancheId(request->tranche_name);
+ strlcpy(LWLockTranches->user_defined[idx].name,
+ request->tranche_name,
+ NAMEDATALEN
+ );
+ LWLockTranches->user_defined[idx].main_array_idx = pos;
- for (j = 0; j < request->num_lwlocks; j++, lock++)
- LWLockInitialize(&lock->lock, tranche);
- }
+ for (int i = 0; i < request->num_lwlocks; i++)
+ LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_FIRST_USER_DEFINED + idx);
}
+
+ /* Cross-check that we agree on the total size with the caller */
+ Assert(pos == numLocks);
}
/*
@@ -566,22 +520,33 @@ InitLWLockAccess(void)
LWLockPadded *
GetNamedLWLockTranche(const char *tranche_name)
{
- int lock_pos;
int i;
+ SpinLockAcquire(ShmemLock);
+ LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
+ SpinLockRelease(ShmemLock);
+
/*
* Obtain the position of base address of LWLock belonging to requested
* tranche_name in MainLWLockArray. LWLocks for named tranches are placed
* in MainLWLockArray after fixed locks.
*/
- lock_pos = NUM_FIXED_LWLOCKS;
- for (i = 0; i < NamedLWLockTrancheRequests; i++)
+ for (i = 0; i < LocalNumUserDefinedTranches; i++)
{
- if (strcmp(NamedLWLockTrancheRequestArray[i].tranche_name,
+ if (strcmp(LWLockTranches->user_defined[i].name,
tranche_name) == 0)
- return &MainLWLockArray[lock_pos];
+ {
+ int lock_pos = LWLockTranches->user_defined[i].main_array_idx;
- lock_pos += NamedLWLockTrancheRequestArray[i].num_lwlocks;
+ /*
+ * GetNamedLWLockTranche() should only be used for locks requested
+ * with RequestNamedLWLockTranche(), not those allocated with
+ * LWLockNewTrancheId().
+ */
+ if (lock_pos == -1)
+ elog(ERROR, "requested tranche was not registered with RequestNamedLWLockTranche()");
+ return &MainLWLockArray[lock_pos];
+ }
}
elog(ERROR, "requested tranche is not registered");
@@ -596,7 +561,7 @@ GetNamedLWLockTranche(const char *tranche_name)
int
LWLockNewTrancheId(const char *name)
{
- int result;
+ int idx;
if (!name)
ereport(ERROR,
@@ -611,12 +576,12 @@ LWLockNewTrancheId(const char *name)
NAMEDATALEN - 1)));
/*
- * We use the ShmemLock spinlock to protect LWLockCounter and
- * LWLockTrancheNames.
+ * We use the ShmemLock spinlock to protect the counter and the tranche
+ * names.
*/
SpinLockAcquire(ShmemLock);
- if (*LWLockCounter - LWTRANCHE_FIRST_USER_DEFINED >= MAX_USER_DEFINED_TRANCHES)
+ if (LWLockTranches->num_user_defined >= MAX_USER_DEFINED_TRANCHES)
{
SpinLockRelease(ShmemLock);
ereport(ERROR,
@@ -625,13 +590,21 @@ LWLockNewTrancheId(const char *name)
MAX_USER_DEFINED_TRANCHES)));
}
- result = (*LWLockCounter)++;
- LocalLWLockCounter = *LWLockCounter;
- strlcpy(LWLockTrancheNames[result - LWTRANCHE_FIRST_USER_DEFINED], name, NAMEDATALEN);
+ /* Allocate an entry in the user_defined array */
+ idx = (LWLockTranches->num_user_defined)++;
+
+ /* update our local copy while we're at it */
+ LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
+
+ /* Initialize it */
+ strlcpy(LWLockTranches->user_defined[idx].name, name, NAMEDATALEN);
+
+ /* the locks are not in the main array */
+ LWLockTranches->user_defined[idx].main_array_idx = -1;
SpinLockRelease(ShmemLock);
- return result;
+ return LWTRANCHE_FIRST_USER_DEFINED + idx;
}
/*
@@ -650,7 +623,6 @@ void
RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
{
NamedLWLockTrancheRequest *request;
- static int NamedLWLockTrancheRequestsAllocated;
if (!process_shmem_requests_in_progress)
elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook");
@@ -667,29 +639,16 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
errdetail("LWLock tranche names must be no longer than %d bytes.",
NAMEDATALEN - 1)));
- if (NamedLWLockTrancheRequestArray == NULL)
- {
- NamedLWLockTrancheRequestsAllocated = 16;
- NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
- MemoryContextAlloc(TopMemoryContext,
- NamedLWLockTrancheRequestsAllocated
- * sizeof(NamedLWLockTrancheRequest));
- }
-
- if (NamedLWLockTrancheRequests >= NamedLWLockTrancheRequestsAllocated)
- {
- int i = pg_nextpower2_32(NamedLWLockTrancheRequests + 1);
-
- NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
- repalloc(NamedLWLockTrancheRequestArray,
- i * sizeof(NamedLWLockTrancheRequest));
- NamedLWLockTrancheRequestsAllocated = i;
- }
+ if (list_length(NamedLWLockTrancheRequests) >= MAX_USER_DEFINED_TRANCHES)
+ ereport(ERROR,
+ (errmsg("maximum number of tranches already registered"),
+ errdetail("No more than %d tranches may be registered.",
+ MAX_USER_DEFINED_TRANCHES)));
- request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests];
+ request = MemoryContextAllocZero(PostmasterContext, sizeof(NamedLWLockTrancheRequest));
strlcpy(request->tranche_name, tranche_name, NAMEDATALEN);
request->num_lwlocks = num_lwlocks;
- NamedLWLockTrancheRequests++;
+ NamedLWLockTrancheRequests = lappend(NamedLWLockTrancheRequests, request);
}
/*
@@ -737,34 +696,36 @@ LWLockReportWaitEnd(void)
static const char *
GetLWTrancheName(uint16 trancheId)
{
+ int idx;
+
/* Built-in tranche or individual LWLock? */
if (trancheId < LWTRANCHE_FIRST_USER_DEFINED)
return BuiltinTrancheNames[trancheId];
/*
- * We only ever add new entries to LWLockTrancheNames, so most lookups can
- * avoid taking the spinlock as long as the backend-local counter
- * (LocalLWLockCounter) is greater than the requested tranche ID. Else,
- * we need to first update the backend-local counter with ShmemLock held
- * before attempting the lookup again. In practice, the latter case is
- * probably rare.
+ * It's an extension tranche, so look in the array.
+ */
+ idx = trancheId - LWTRANCHE_FIRST_USER_DEFINED;
+
+ /*
+ * We only ever add new entries to LWLockTranches->user_defined, so most
+ * lookups can avoid taking the spinlock as long as the backend-local
+ * counter (LocalNumUserDefinedTranches) is greater than the requested
+ * tranche ID. Else, we need to first update the backend-local counter
+ * with ShmemLock held before attempting the lookup again. In practice,
+ * the latter case is probably rare.
*/
- if (trancheId >= LocalLWLockCounter)
+ if (idx >= LocalNumUserDefinedTranches)
{
SpinLockAcquire(ShmemLock);
- LocalLWLockCounter = *LWLockCounter;
+ LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
SpinLockRelease(ShmemLock);
- if (trancheId >= LocalLWLockCounter)
+ if (idx >= LocalNumUserDefinedTranches)
elog(ERROR, "tranche %d is not registered", trancheId);
}
- /*
- * It's an extension tranche, so look in LWLockTrancheNames.
- */
- trancheId -= LWTRANCHE_FIRST_USER_DEFINED;
-
- return LWLockTrancheNames[trancheId];
+ return LWLockTranches->user_defined[idx].name;
}
/*
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 9a0290391d0..30557631eb8 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -74,12 +74,9 @@ typedef union LWLockPadded
extern PGDLLIMPORT LWLockPadded *MainLWLockArray;
/* forward declaration of private type for use only by lwlock.c */
-typedef struct NamedLWLockTrancheRequest NamedLWLockTrancheRequest;
+typedef struct LWLockTrancheShmemData LWLockTrancheShmemData;
-extern PGDLLIMPORT char **LWLockTrancheNames;
-extern PGDLLIMPORT int NamedLWLockTrancheRequests;
-extern PGDLLIMPORT NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray;
-extern PGDLLIMPORT int *LWLockCounter;
+extern PGDLLIMPORT LWLockTrancheShmemData *LWLockTranches;
/*
* It's a bit odd to declare NUM_BUFFER_PARTITIONS and NUM_LOCK_PARTITIONS
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index dbbec84b222..b82694a3680 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1577,6 +1577,7 @@ LWLock
LWLockHandle
LWLockMode
LWLockPadded
+LWLockTrancheShmemData
LZ4F_compressionContext_t
LZ4F_decompressOptions_t
LZ4F_decompressionContext_t
--
2.47.3
v1-0003-Use-a-separate-spinlock-to-protect-LWLockTranches.patch (text/x-patch, 5.5K) download
From f451b44f81e57759a876fc9ca8360826da0537d9 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 26 Mar 2026 13:26:56 +0200
Subject: [PATCH v1 3/5] Use a separate spinlock to protect LWLockTranches
Previously we reused the shmem allocator's ShmemLock for this. For the
sake of modularity, it's more clear to have a dedicated lock. Now that
lwlock.c has its own shared memory struct, this is easy to do.
---
src/backend/storage/ipc/shmem.c | 8 +++-----
src/backend/storage/lmgr/lwlock.c | 24 +++++++++++++-----------
src/include/storage/shmem.h | 1 -
3 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 0fb3bc59929..f392faa534f 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -105,7 +105,6 @@ static void *ShmemBase; /* start address of shared memory */
static void *ShmemEnd; /* end+1 address of shared memory */
static ShmemAllocatorData *ShmemAllocator;
-slock_t *ShmemLock; /* points to ShmemAllocator->shmem_lock */
static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
/* To get reliable results for NUMA inquiry we need to "touch pages" once */
@@ -166,7 +165,6 @@ InitShmemAllocator(PGShmemHeader *seghdr)
ShmemAllocator->free_offset = offset;
}
- ShmemLock = &ShmemAllocator->shmem_lock;
ShmemSegHdr = seghdr;
ShmemBase = seghdr;
ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
@@ -200,7 +198,7 @@ InitShmemAllocator(PGShmemHeader *seghdr)
*
* Throws error if request cannot be satisfied.
*
- * Assumes ShmemLock and ShmemSegHdr are initialized.
+ * Assumes ShmemSegHdr are initialized.
*/
void *
ShmemAlloc(Size size)
@@ -259,7 +257,7 @@ ShmemAllocRaw(Size size, Size *allocated_size)
Assert(ShmemSegHdr != NULL);
- SpinLockAcquire(ShmemLock);
+ SpinLockAcquire(&ShmemAllocator->shmem_lock);
newStart = ShmemAllocator->free_offset;
@@ -272,7 +270,7 @@ ShmemAllocRaw(Size size, Size *allocated_size)
else
newSpace = NULL;
- SpinLockRelease(ShmemLock);
+ SpinLockRelease(&ShmemAllocator->shmem_lock);
/* note this assert is okay with newSpace == NULL */
Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 2e3f2f2a6ff..4c01afce2f7 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -191,6 +191,8 @@ typedef struct LWLockTrancheShmemData
} user_defined[MAX_USER_DEFINED_TRANCHES];
int num_user_defined; /* 'user_defined' entries in use */
+
+ slock_t lock; /* protects the above */
} LWLockTrancheShmemData;
LWLockTrancheShmemData *LWLockTranches;
@@ -437,6 +439,7 @@ CreateLWLocks(void)
ShmemAlloc(sizeof(LWLockTrancheShmemData));
/* Initialize the dynamic-allocation counter for tranches */
+ SpinLockInit(&LWLockTranches->lock);
LWLockTranches->num_user_defined = 0;
/* Allocate and initialize the main array */
@@ -522,9 +525,9 @@ GetNamedLWLockTranche(const char *tranche_name)
{
int i;
- SpinLockAcquire(ShmemLock);
+ SpinLockAcquire(&LWLockTranches->lock);
LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
- SpinLockRelease(ShmemLock);
+ SpinLockRelease(&LWLockTranches->lock);
/*
* Obtain the position of base address of LWLock belonging to requested
@@ -576,14 +579,13 @@ LWLockNewTrancheId(const char *name)
NAMEDATALEN - 1)));
/*
- * We use the ShmemLock spinlock to protect the counter and the tranche
- * names.
+ * We use the spinlock to protect the counter and the tranche names.
*/
- SpinLockAcquire(ShmemLock);
+ SpinLockAcquire(&LWLockTranches->lock);
if (LWLockTranches->num_user_defined >= MAX_USER_DEFINED_TRANCHES)
{
- SpinLockRelease(ShmemLock);
+ SpinLockRelease(&LWLockTranches->lock);
ereport(ERROR,
(errmsg("maximum number of tranches already registered"),
errdetail("No more than %d tranches may be registered.",
@@ -602,7 +604,7 @@ LWLockNewTrancheId(const char *name)
/* the locks are not in the main array */
LWLockTranches->user_defined[idx].main_array_idx = -1;
- SpinLockRelease(ShmemLock);
+ SpinLockRelease(&LWLockTranches->lock);
return LWTRANCHE_FIRST_USER_DEFINED + idx;
}
@@ -712,14 +714,14 @@ GetLWTrancheName(uint16 trancheId)
* lookups can avoid taking the spinlock as long as the backend-local
* counter (LocalNumUserDefinedTranches) is greater than the requested
* tranche ID. Else, we need to first update the backend-local counter
- * with ShmemLock held before attempting the lookup again. In practice,
- * the latter case is probably rare.
+ * with the spinlock held before attempting the lookup again. In
+ * practice, the latter case is probably rare.
*/
if (idx >= LocalNumUserDefinedTranches)
{
- SpinLockAcquire(ShmemLock);
+ SpinLockAcquire(&LWLockTranches->lock);
LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
- SpinLockRelease(ShmemLock);
+ SpinLockRelease(&LWLockTranches->lock);
if (idx >= LocalNumUserDefinedTranches)
elog(ERROR, "tranche %d is not registered", trancheId);
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 0de8a36429b..2a9e9becd26 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -26,7 +26,6 @@
/* shmem.c */
-extern PGDLLIMPORT slock_t *ShmemLock;
typedef struct PGShmemHeader PGShmemHeader; /* avoid including
* storage/pg_shmem.h here */
extern void InitShmemAllocator(PGShmemHeader *seghdr);
--
2.47.3
v1-0004-Use-ShmemInitStruct-to-allocate-lwlock.c-s-shared.patch (text/x-patch, 7.5K) download
From d5362cffef3de02259841ce4b049b7bd8898bef9 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 26 Mar 2026 13:26:59 +0200
Subject: [PATCH v1 4/5] Use ShmemInitStruct to allocate lwlock.c's shared
memory
It's nice to have them show up in pg_shmem_allocations like all other
shmem areas. ShmemInitStruct() depends on ShmemIndexLock, but only
after postmaster startup.
---
src/backend/postmaster/launch_backend.c | 3 --
src/backend/storage/ipc/ipci.c | 6 ++--
src/backend/storage/ipc/shmem.c | 15 ++++++----
src/backend/storage/lmgr/lwlock.c | 39 ++++++++++++++++---------
src/include/storage/lwlock.h | 7 +----
5 files changed, 39 insertions(+), 31 deletions(-)
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 8c134eaca88..8aaf427df48 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -99,7 +99,6 @@ typedef struct
#ifdef USE_INJECTION_POINTS
struct InjectionPointsCtl *ActiveInjectionPoints;
#endif
- LWLockTrancheShmemData *LWLockTranches;
LWLockPadded *MainLWLockArray;
PROC_HDR *ProcGlobal;
PGPROC *AuxiliaryProcs;
@@ -726,7 +725,6 @@ save_backend_variables(BackendParameters *param,
param->ActiveInjectionPoints = ActiveInjectionPoints;
#endif
- param->LWLockTranches = LWLockTranches;
param->MainLWLockArray = MainLWLockArray;
param->ProcGlobal = ProcGlobal;
param->AuxiliaryProcs = AuxiliaryProcs;
@@ -982,7 +980,6 @@ restore_backend_variables(BackendParameters *param)
ActiveInjectionPoints = param->ActiveInjectionPoints;
#endif
- LWLockTranches = param->LWLockTranches;
MainLWLockArray = param->MainLWLockArray;
ProcGlobal = param->ProcGlobal;
AuxiliaryProcs = param->AuxiliaryProcs;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 3d3f153809b..036db3fa514 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -249,10 +249,8 @@ CreateSharedMemoryAndSemaphores(void)
static void
CreateOrAttachShmemStructs(void)
{
- /*
- * Now initialize LWLocks, which do shared memory allocation.
- */
- CreateLWLocks();
+ /* Start with initializing LWLock tranches */
+ LWLockShmemInit();
dsm_shmem_init();
DSMRegistryShmemInit();
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index f392faa534f..339fbba3404 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -379,7 +379,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Assert(ShmemIndex != NULL);
- LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
+ if (IsUnderPostmaster)
+ LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
/* look it up in the shmem index */
result = (ShmemIndexEnt *)
@@ -387,7 +388,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
if (!result)
{
- LWLockRelease(ShmemIndexLock);
+ if (IsUnderPostmaster)
+ LWLockRelease(ShmemIndexLock);
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("could not create ShmemIndex entry for data structure \"%s\"",
@@ -403,7 +405,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
*/
if (result->size != size)
{
- LWLockRelease(ShmemIndexLock);
+ if (IsUnderPostmaster)
+ LWLockRelease(ShmemIndexLock);
ereport(ERROR,
(errmsg("ShmemIndex entry size is wrong for data structure"
" \"%s\": expected %zu, actual %zu",
@@ -421,7 +424,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
{
/* out of memory; remove the failed ShmemIndex entry */
hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
- LWLockRelease(ShmemIndexLock);
+ if (IsUnderPostmaster)
+ LWLockRelease(ShmemIndexLock);
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough shared memory for data structure"
@@ -433,7 +437,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
result->location = structPtr;
}
- LWLockRelease(ShmemIndexLock);
+ if (IsUnderPostmaster)
+ LWLockRelease(ShmemIndexLock);
Assert(ShmemAddrIsValid(structPtr));
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 4c01afce2f7..7bb005a5833 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -193,9 +193,12 @@ typedef struct LWLockTrancheShmemData
int num_user_defined; /* 'user_defined' entries in use */
slock_t lock; /* protects the above */
+
+ /* Size of MainLWLockArray */
+ int num_main_array_locks;
} LWLockTrancheShmemData;
-LWLockTrancheShmemData *LWLockTranches;
+static LWLockTrancheShmemData *LWLockTranches;
/* backend-local copy of NamedLWLockTranches->num_user_defined */
static int LocalNumUserDefinedTranches;
@@ -405,7 +408,8 @@ NumLWLocksForNamedTranches(void)
}
/*
- * Compute shmem space needed for LWLocks and named tranches.
+ * Compute shmem space needed for user-defined tranches and the main LWLock
+ * array.
*/
Size
LWLockShmemSize(void)
@@ -424,27 +428,36 @@ LWLockShmemSize(void)
}
/*
- * Allocate shmem space for the main LWLock array and all tranches and
- * initialize it.
+ * Allocate and initialize shmem for user-defined LWLock tranches and the main
+ * LWLock array.
*/
void
-CreateLWLocks(void)
+LWLockShmemInit(void)
{
int numLocks;
+ bool found;
- if (!IsUnderPostmaster)
+ LWLockTranches = (LWLockTrancheShmemData *)
+ ShmemInitStruct("LWLock tranches", sizeof(LWLockTrancheShmemData), &found);
+ if (!found)
{
- /* Allocate space for LWLockTranches */
- LWLockTranches = (LWLockTrancheShmemData *)
- ShmemAlloc(sizeof(LWLockTrancheShmemData));
+ /* Calculate total number of locks needed in the main array */
+ LWLockTranches->num_main_array_locks =
+ NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches();
/* Initialize the dynamic-allocation counter for tranches */
- SpinLockInit(&LWLockTranches->lock);
LWLockTranches->num_user_defined = 0;
- /* Allocate and initialize the main array */
- numLocks = NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches();
- MainLWLockArray = (LWLockPadded *) ShmemAlloc(numLocks * sizeof(LWLockPadded));
+ SpinLockInit(&LWLockTranches->lock);
+ }
+
+ /* Allocate and initialize the main array */
+ numLocks = LWLockTranches->num_main_array_locks;
+ MainLWLockArray = (LWLockPadded *)
+ ShmemInitStruct("Main LWLock array", numLocks * sizeof(LWLockPadded), &found);
+ if (!found)
+ {
+ /* Initialize all LWLocks */
InitializeLWLocks(numLocks);
}
}
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 30557631eb8..61f0dbe749a 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -73,11 +73,6 @@ typedef union LWLockPadded
extern PGDLLIMPORT LWLockPadded *MainLWLockArray;
-/* forward declaration of private type for use only by lwlock.c */
-typedef struct LWLockTrancheShmemData LWLockTrancheShmemData;
-
-extern PGDLLIMPORT LWLockTrancheShmemData *LWLockTranches;
-
/*
* It's a bit odd to declare NUM_BUFFER_PARTITIONS and NUM_LOCK_PARTITIONS
* here, but we need them to figure out offsets within MainLWLockArray, and
@@ -132,7 +127,7 @@ extern bool LWLockWaitForVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 oldv
extern void LWLockUpdateVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 val);
extern Size LWLockShmemSize(void);
-extern void CreateLWLocks(void);
+extern void LWLockShmemInit(void);
extern void InitLWLockAccess(void);
extern const char *GetLWLockIdentifier(uint32 classId, uint16 eventId);
--
2.47.3
v1-0005-Move-ShmemIndexLock-into-ShmemAllocator.patch (text/x-patch, 6.1K) download
From 1c48b2d24460b288fe2c30d42d2cb00ecb5b2a59 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 26 Mar 2026 13:27:59 +0200
Subject: [PATCH v1 5/5] Move ShmemIndexLock into ShmemAllocator
This makes the lock available even before LWLockShmemInit() has been
called, removing the need to pass MainLWLockArray to child backends
via BackendParameters in EXEC_BACKEND mode.
---
src/backend/postmaster/launch_backend.c | 3 ---
src/backend/storage/ipc/shmem.c | 25 ++++++++++---------
.../utils/activity/wait_event_names.txt | 2 +-
src/include/storage/lwlocklist.h | 3 ++-
4 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 8aaf427df48..434e0643022 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -99,7 +99,6 @@ typedef struct
#ifdef USE_INJECTION_POINTS
struct InjectionPointsCtl *ActiveInjectionPoints;
#endif
- LWLockPadded *MainLWLockArray;
PROC_HDR *ProcGlobal;
PGPROC *AuxiliaryProcs;
PGPROC *PreparedXactProcs;
@@ -725,7 +724,6 @@ save_backend_variables(BackendParameters *param,
param->ActiveInjectionPoints = ActiveInjectionPoints;
#endif
- param->MainLWLockArray = MainLWLockArray;
param->ProcGlobal = ProcGlobal;
param->AuxiliaryProcs = AuxiliaryProcs;
param->PreparedXactProcs = PreparedXactProcs;
@@ -980,7 +978,6 @@ restore_backend_variables(BackendParameters *param)
ActiveInjectionPoints = param->ActiveInjectionPoints;
#endif
- MainLWLockArray = param->MainLWLockArray;
ProcGlobal = param->ProcGlobal;
AuxiliaryProcs = param->AuxiliaryProcs;
PreparedXactProcs = param->PreparedXactProcs;
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 339fbba3404..ec21eb4a191 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -90,12 +90,16 @@
typedef struct ShmemAllocatorData
{
Size free_offset; /* offset to first free space from ShmemBase */
- HASHHDR *index; /* location of ShmemIndex */
- /* protects shared memory and LWLock allocation */
+ /* protects 'free_offset' */
slock_t shmem_lock;
+
+ HASHHDR *index; /* location of ShmemIndex */
+ LWLock index_lock;
} ShmemAllocatorData;
+#define ShmemIndexLock (&ShmemAllocator->index_lock)
+
static void *ShmemAllocRaw(Size size, Size *allocated_size);
/* shared memory global variables */
@@ -163,6 +167,8 @@ InitShmemAllocator(PGShmemHeader *seghdr)
{
SpinLockInit(&ShmemAllocator->shmem_lock);
ShmemAllocator->free_offset = offset;
+
+ LWLockInitialize(&ShmemAllocator->index_lock, LWTRANCHE_SHMEM_INDEX);
}
ShmemSegHdr = seghdr;
@@ -379,8 +385,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Assert(ShmemIndex != NULL);
- if (IsUnderPostmaster)
- LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
+ LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
/* look it up in the shmem index */
result = (ShmemIndexEnt *)
@@ -388,8 +393,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
if (!result)
{
- if (IsUnderPostmaster)
- LWLockRelease(ShmemIndexLock);
+ LWLockRelease(ShmemIndexLock);
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("could not create ShmemIndex entry for data structure \"%s\"",
@@ -405,8 +409,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
*/
if (result->size != size)
{
- if (IsUnderPostmaster)
- LWLockRelease(ShmemIndexLock);
+ LWLockRelease(ShmemIndexLock);
ereport(ERROR,
(errmsg("ShmemIndex entry size is wrong for data structure"
" \"%s\": expected %zu, actual %zu",
@@ -424,8 +427,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
{
/* out of memory; remove the failed ShmemIndex entry */
hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
- if (IsUnderPostmaster)
- LWLockRelease(ShmemIndexLock);
+ LWLockRelease(ShmemIndexLock);
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough shared memory for data structure"
@@ -437,8 +439,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
result->location = structPtr;
}
- if (IsUnderPostmaster)
- LWLockRelease(ShmemIndexLock);
+ LWLockRelease(ShmemIndexLock);
Assert(ShmemAddrIsValid(structPtr));
diff --git a/src/backend/utils/activity/wait_event_names.txt b/src/backend/utils/activity/wait_event_names.txt
index 4aa864fe3c3..6be80d2daad 100644
--- a/src/backend/utils/activity/wait_event_names.txt
+++ b/src/backend/utils/activity/wait_event_names.txt
@@ -321,7 +321,6 @@ ABI_compatibility:
Section: ClassName - WaitEventLWLock
-ShmemIndex "Waiting to find or allocate space in shared memory."
OidGen "Waiting to allocate a new OID."
XidGen "Waiting to allocate a new transaction ID."
ProcArray "Waiting to access the shared per-process data structures (typically, to get a snapshot or report a session's transaction ID)."
@@ -412,6 +411,7 @@ SubtransSLRU "Waiting to access the sub-transaction SLRU cache."
XactSLRU "Waiting to access the transaction status SLRU cache."
ParallelVacuumDSA "Waiting for parallel vacuum dynamic shared memory allocation."
AioUringCompletion "Waiting for another process to complete IO via io_uring."
+ShmemIndex "Waiting to find or allocate space in shared memory."
# No "ABI_compatibility" region here as WaitEventLWLock has its own C code.
diff --git a/src/include/storage/lwlocklist.h b/src/include/storage/lwlocklist.h
index e94ebce95b9..59ee097977d 100644
--- a/src/include/storage/lwlocklist.h
+++ b/src/include/storage/lwlocklist.h
@@ -32,7 +32,7 @@
*/
/* 0 is available; was formerly BufFreelistLock */
-PG_LWLOCK(1, ShmemIndex)
+/* 1 was ShmemIndex */
PG_LWLOCK(2, OidGen)
PG_LWLOCK(3, XidGen)
PG_LWLOCK(4, ProcArray)
@@ -137,3 +137,4 @@ PG_LWLOCKTRANCHE(SUBTRANS_SLRU, SubtransSLRU)
PG_LWLOCKTRANCHE(XACT_SLRU, XactSLRU)
PG_LWLOCKTRANCHE(PARALLEL_VACUUM_DSA, ParallelVacuumDSA)
PG_LWLOCKTRANCHE(AIO_URING_COMPLETION, AioUringCompletion)
+PG_LWLOCKTRANCHE(SHMEM_INDEX, ShmemIndex)
--
2.47.3
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-26 14:37 Nathan Bossart <nathandbossart@gmail.com>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Nathan Bossart @ 2026-03-26 14:37 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Thu, Mar 26, 2026 at 02:16:52PM +0200, Heikki Linnakangas wrote:
> At postmaster startup, NamedLWLockTrancheRequests points to a
> backend-private array. But after startup, and always in backends, it points
> to a copy in shared memory and LocalNamedLWLockTrancheRequestArray is used
> to hold the original. It took me a while to realize that
> NamedLWLockTrancheRequests in shared memory is *not* updated when you call
> LWLockNewTrancheId(), it only holds the requests made with
> RequestNamedLWLockTranche() before startup.
Right. LocalNamedLWLockTrancheRequestArray is needed so that we can
re-initialize shared memory after a crash. See commit c3cc2ab87d.
> I propose the attached refactorings to make this less confusing. See commit
> messages for details.
Thanks for doing this, Heikki. I agree that we ought to make this stuff
cleaner. I've asked Sami Imseih, who worked on LWLocks with me last year,
to look at this patch set, too.
> Subject: [PATCH v1 1/5] Rename MAX_NAMED_TRANCHES to MAX_USER_DEFINED_TRANCHES
Seems fine to me.
0002:
> + foreach(lc, NamedLWLockTrancheRequests)
nitpick: These foreach loops seem like good opportunities to use
foreach_ptr.
The comment atop NumLWLocksForNamedTranches might benefit from mentioning
RequestNamedLWLockTranche() and the fact that it only works in the
postmaster. Perhaps an assertion is warranted, too.
+ SpinLockAcquire(ShmemLock);
+ LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
+ SpinLockRelease(ShmemLock);
Not critical, but it might be worth making num_user_defined an atomic.
Overall, 0002 looks reasonable to me upon a first read-through.
> Subject: [PATCH v1 3/5] Use a separate spinlock to protect LWLockTranches
Seems fine to me.
0004:
> +++ b/src/backend/storage/ipc/shmem.c
> @@ -379,7 +379,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
>
> Assert(ShmemIndex != NULL);
>
> - LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
> + if (IsUnderPostmaster)
> + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
Am I understanding that we assume ShmemInitStruct() is only called by the
postmaster when there are no other backends yet?
0005:
> - if (IsUnderPostmaster)
> - LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
> + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
Oh, this reverts many of these changes from 0004. Maybe the patches could
be reordered to avoid this?
--
nathan
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-26 15:21 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-26 15:21 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 26/03/2026 16:37, Nathan Bossart wrote:
> On Thu, Mar 26, 2026 at 02:16:52PM +0200, Heikki Linnakangas wrote:
>> 0002:
>>
>> + foreach(lc, NamedLWLockTrancheRequests)
>
> nitpick: These foreach loops seem like good opportunities to use
> foreach_ptr.
>
> The comment atop NumLWLocksForNamedTranches might benefit from mentioning
> RequestNamedLWLockTranche() and the fact that it only works in the
> postmaster. Perhaps an assertion is warranted, too.
There's already this check in RequestNamedLWLockTranche():
if (!process_shmem_requests_in_progress)
elog(FATAL, "cannot request additional LWLocks outside
shmem_request_hook");
shmem_request_hooks are only called early at postmaster startup.
> + SpinLockAcquire(ShmemLock);
> + LocalNumUserDefinedTranches = LWLockTranches->num_user_defined;
> + SpinLockRelease(ShmemLock);
>
> Not critical, but it might be worth making num_user_defined an atomic.
Yeah I considered that. The lock is still needed in
LWLockNewTrancheId(), though, to prevent two concurrent
LWLockNewTrancheId() calls from running concurrently. Using an atomic
would allow the extra optimization of reading the value without
acquiring spinlock, but it seems more clear to have a clear-cut rule
that you must always hold the spinlock whenever accessing the field.
> 0004:
>
>> +++ b/src/backend/storage/ipc/shmem.c
>> @@ -379,7 +379,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
>>
>> Assert(ShmemIndex != NULL);
>>
>> - LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
>> + if (IsUnderPostmaster)
>> + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
>
> Am I understanding that we assume ShmemInitStruct() is only called by the
> postmaster when there are no other backends yet?
Yeah. LWLockAcquire has this:
/*
* We can't wait if we haven't got a PGPROC. This should only occur
* during bootstrap or shared memory initialization. Put an Assert
here
* to catch unsafe coding practices.
*/
Assert(!(proc == NULL && IsUnderPostmaster));
To be honest I didn't realize we tolerate that, calling LWLockAcquire in
postmaster, until I started to work on this. It might be worth having
some extra sanity checks here, to e.g. to throw an error if
LWLockAcquire is called from postmaster after startup. But this isn't new.
> 0005:
>
>> - if (IsUnderPostmaster)
>> - LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
>> + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
>
> Oh, this reverts many of these changes from 0004. Maybe the patches could
> be reordered to avoid this?
Makes sense.
Thanks for the review!
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-26 16:34 Sami Imseih <samimseih@gmail.com>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Sami Imseih @ 2026-03-26 16:34 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Hi,
Thanks for the patches!
> I propose the attached refactorings to make this less confusing. See
> commit messages for details.
I only took a look at 0001 so far, and I do agree with this statement
in the commit message:
"The "user defined" term was already used in LWTRANCHE_FIRST_USER_DEFINED,
so let's standardize on that to mean tranches allocated with either
RequestNamedLWLockTranche() or LWLockNewTrancheId()."
I do wonder if 0001 is going far enough though.
Instead of just standardizing that "user defined" could mean tranches allocated
with RequestNamedLWLockTranche() or LWLockNewTrancheId(), how about we also
rename these APIs to reflect that as well? This way we remove all concept of
"named tranche" which is what it sounds like to me you are proposing.
rename RequestNamedLWLockTranche() to RequestUserDefinedLWLockTranche()
and LWLockNewTrancheId() to RegisterUserDefinedLWLockTranche()
RequestNamedLWLockTranche() requests the lwlock at shmem_request time,
which is later registered via LWLockNewTrancheId() when lwlocks are
initialized by the postmaster.
Also, the name LWLockNewTrancheId() is selling what this function does
too short.
It does return a new tranche ID, but it also takes in a user-defined tranche
name and copies ("registers") that name into LWLockTrancheNames.
v19 is already changing the signature of LWLockNewTrancheId(), so maybe
improving the names of these APIs makes sense to do.
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-26 16:57 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-26 16:57 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Thanks!
On 26/03/2026 18:34, Sami Imseih wrote:
>> I propose the attached refactorings to make this less confusing. See
>> commit messages for details.
>
> I only took a look at 0001 so far, and I do agree with this statement
> in the commit message:
>
> "The "user defined" term was already used in LWTRANCHE_FIRST_USER_DEFINED,
> so let's standardize on that to mean tranches allocated with either
> RequestNamedLWLockTranche() or LWLockNewTrancheId()."
>
> I do wonder if 0001 is going far enough though.
>
> Instead of just standardizing that "user defined" could mean tranches allocated
> with RequestNamedLWLockTranche() or LWLockNewTrancheId(), how about we also
> rename these APIs to reflect that as well? This way we remove all concept of
> "named tranche" which is what it sounds like to me you are proposing.
>
> rename RequestNamedLWLockTranche() to RequestUserDefinedLWLockTranche()
> and LWLockNewTrancheId() to RegisterUserDefinedLWLockTranche()
I'd rather not change RequestNamedLWLockTranche(), because I think
LWLockNewTrancheId() is better and should be used in new code. I
consider RequestNamedLWLockTranche() to be a legacy function, for
backwards compatibility.
> RequestNamedLWLockTranche() requests the lwlock at shmem_request time,
> which is later registered via LWLockNewTrancheId() when lwlocks are
> initialized by the postmaster.
>
> Also, the name LWLockNewTrancheId() is selling what this function does
> too short.
> It does return a new tranche ID, but it also takes in a user-defined tranche
> name and copies ("registers") that name into LWLockTrancheNames.
>
> v19 is already changing the signature of LWLockNewTrancheId(), so maybe
> improving the names of these APIs makes sense to do.
Oh, I didn't realize we changed the LWLockNewTrancheId() signature!
Yeah, if we're changing it anyway, we might as well rename it. I'm not
sure if I like RegisterUserDefinedLWLockTranche() better, but let's
think it through.
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-26 22:03 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-26 22:03 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 26/03/2026 18:57, Heikki Linnakangas wrote:
> Thanks!
>
> On 26/03/2026 18:34, Sami Imseih wrote:
>>> I propose the attached refactorings to make this less confusing. See
>>> commit messages for details.
>>
>> I only took a look at 0001 so far, and I do agree with this statement
>> in the commit message:
I committed these now, but I'm all ears if you still have comments on
the rest of the patches.
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 03:57 Sami Imseih <samimseih@gmail.com>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Sami Imseih @ 2026-03-27 03:57 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Hi,
> > Thanks!
> >
> > On 26/03/2026 18:34, Sami Imseih wrote:
> >>> I propose the attached refactorings to make this less confusing. See
> >>> commit messages for details.
> >>
> >> I only took a look at 0001 so far, and I do agree with this statement
> >> in the commit message:
>
> I committed these now, but I'm all ears if you still have comments on
> the rest of the patches.
Sorry for the delay. I see you committed the rest. The only issue I found
is with d6eba30
+/* backend-local copy of NamedLWLockTranches->num_user_defined */
+static int LocalNumUserDefinedTranches;
The comment here should reference "LWLockTranches->num_user_defined "
instead.
>> rename RequestNamedLWLockTranche() to RequestUserDefinedLWLockTranche()
>> and LWLockNewTrancheId() to RegisterUserDefinedLWLockTranche()
> I'd rather not change RequestNamedLWLockTranche(), because I think
> LWLockNewTrancheId() is better and should be used in new code.
That's fair.
>> v19 is already changing the signature of LWLockNewTrancheId(), so maybe
>> improving the names of these APIs makes sense to do.
> Oh, I didn't realize we changed the LWLockNewTrancheId() signature!
> Yeah, if we're changing it anyway, we might as well rename it. I'm not
> sure if I like RegisterUserDefinedLWLockTranche() better, but let's
> think it through.
Maybe, RegisterNewLWLockTrancheId() could be more meaningful?
Also, there are a few places in lwlock.c where "named tranches" is mentioned.
Maybe we should just say "user-defined tranches" instead?
--
Sami Imseih
Amazon Web Services (AWS)
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 04:49 Sami Imseih <samimseih@gmail.com>
parent: Sami Imseih <samimseih@gmail.com>
0 siblings, 1 reply; 18+ messages in thread
From: Sami Imseih @ 2026-03-27 04:49 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
> +/* backend-local copy of NamedLWLockTranches->num_user_defined */
> +static int LocalNumUserDefinedTranches;
> The comment here should reference "LWLockTranches->num_user_defined "
> instead.
> Also, there are a few places in lwlock.c where "named tranches" is mentioned.
> Maybe we should just say "user-defined tranches" instead?
Like the attached.
--
Sami
Attachments:
v1-0001-fix-some-comments-for-lwlock-tranches.patch (application/octet-stream, 2.0K) download
From 3f8053adde0da2c99fd1e02f464a7247c73f0887 Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
Date: Fri, 27 Mar 2026 04:46:55 +0000
Subject: [PATCH v1 1/1] fix some comments for lwlock tranches
---
src/backend/storage/lmgr/lwlock.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 241f1f08430..2ee385698e5 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -200,7 +200,7 @@ typedef struct LWLockTrancheShmemData
static LWLockTrancheShmemData *LWLockTranches;
-/* backend-local copy of NamedLWLockTranches->num_user_defined */
+/* backend-local copy of LWLockTranches->num_user_defined */
static int LocalNumUserDefinedTranches;
/*
@@ -460,7 +460,7 @@ LWLockShmemInit(void)
}
/*
- * Initialize LWLocks that are fixed and those belonging to named tranches.
+ * Initialize LWLocks that are fixed and those belonging to user-defined tranches.
*/
static void
InitializeLWLocks(int numLocks)
@@ -487,7 +487,7 @@ InitializeLWLocks(int numLocks)
LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);
/*
- * Copy the info about any named tranches into shared memory (so that
+ * Copy the info about any user-defined tranches into shared memory (so that
* other processes can see it), and initialize the requested LWLocks.
*/
Assert(pos == NUM_FIXED_LWLOCKS);
@@ -536,8 +536,9 @@ GetNamedLWLockTranche(const char *tranche_name)
/*
* Obtain the position of base address of LWLock belonging to requested
- * tranche_name in MainLWLockArray. LWLocks for named tranches are placed
- * in MainLWLockArray after fixed locks.
+ * tranche_name in MainLWLockArray. LWLocks for user-defined tranches
+ * requested with RequestNamedLWLockTranche() are placed in
+ * MainLWLockArray after fixed locks.
*/
for (int i = 0; i < LocalNumUserDefinedTranches; i++)
{
--
2.47.3
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 09:45 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Sami Imseih <samimseih@gmail.com>
0 siblings, 2 replies; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-27 09:45 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 27/03/2026 06:49, Sami Imseih wrote:
>> +/* backend-local copy of NamedLWLockTranches->num_user_defined */
>> +static int LocalNumUserDefinedTranches;
>
>> The comment here should reference "LWLockTranches->num_user_defined "
>> instead.
>
>> Also, there are a few places in lwlock.c where "named tranches" is mentioned.
>> Maybe we should just say "user-defined tranches" instead?
>
> Like the attached.
> @@ -460,7 +460,7 @@ LWLockShmemInit(void)
> }
>
> /*
> - * Initialize LWLocks that are fixed and those belonging to named tranches.
> + * Initialize LWLocks that are fixed and those belonging to user-defined tranches.
> */
> static void
> InitializeLWLocks(int numLocks)
Only tranches requested with RequestNamedLWLockTranche() have locks in
the main array, so I reworded this some more to:
/*
* Initialize LWLocks for built-in tranches and those requested with
* RequestNamedLWLockTranche().
*/
Committed with that little change, thanks!
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 14:53 Sami Imseih <samimseih@gmail.com>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
1 sibling, 0 replies; 18+ messages in thread
From: Sami Imseih @ 2026-03-27 14:53 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
> Committed with that little change, thanks!
Thanks!
I think there is one more comment cleanup in lwlock.c
/*
- * This points to the main array of LWLocks in shared memory. Backends inherit
- * the pointer by fork from the postmaster (except in the EXEC_BACKEND case,
- * where we have special measures to pass it down).
+ * This points to the main array of LWLocks in shared memory.
*/
we no longer need to take special measures to pass down MainLWLockArray
through the BackendParameters.
--
Sami
Attachments:
v1-0001-Remove-another-outdated-comment-regading-MainLWLo.patch (application/octet-stream, 990B) download
From e13f8683b3fc4ef08ad7892e8f6b55c74ab52fc5 Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
Date: Fri, 27 Mar 2026 14:49:02 +0000
Subject: [PATCH v1 1/1] Remove another outdated comment regading
MainLWLockArray
---
src/backend/storage/lmgr/lwlock.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 7a68071302a..ade5c652e37 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -146,9 +146,7 @@ StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
"missing entries in BuiltinTrancheNames[]");
/*
- * This points to the main array of LWLocks in shared memory. Backends inherit
- * the pointer by fork from the postmaster (except in the EXEC_BACKEND case,
- * where we have special measures to pass it down).
+ * This points to the main array of LWLocks in shared memory.
*/
LWLockPadded *MainLWLockArray = NULL;
--
2.47.3
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 21:22 Andres Freund <andres@anarazel.de>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
1 sibling, 1 reply; 18+ messages in thread
From: Andres Freund @ 2026-03-27 21:22 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Sami Imseih <samimseih@gmail.com>; Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Hi,
On 2026-03-27 11:45:56 +0200, Heikki Linnakangas wrote:
> Committed with that little change, thanks!
This seems to have broken buildfarm animal batta:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=batta&dt=2026-03-27%2002%3A05%3A01
# Running: pg_rewind --debug --source-pgdata /home/admin/batta/buildroot/HEAD/pgsql.build/src/bin/pg_rewind/tmp_check/t_001_basic_standby_local_data/pgdata --target-pgdata /home/admin/batta/buildroot/HEAD/pgsql.build/src/bin/pg_rewind/tmp_check/t_001_basic_primary_local_data/pgdata --no-sync --config-file /home/admin/batta/buildroot/HEAD/pgsql.build/src/bin/pg_rewind/tmp_check/tmp_test_QbsG/primary-postgresql.conf.tmp
pg_rewind: executing "/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres" for target server to complete crash recovery
TRAP: failed Assert("MemoryContextIsValid(context)"), File: "mcxt.c", Line: 1270, PID: 230491
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(ExceptionalCondition+0x54)[0xaaaae186c204]
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(MemoryContextAllocExtended+0x0)[0xaaaae18a2a24]
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(RequestNamedLWLockTranche+0x6c)[0xaaaae16e7310]
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(process_shmem_requests+0x28)[0xaaaae1881628]
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(PostgresSingleUserMain+0xc4)[0xaaaae1701a34]
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(main+0x6ac)[0xaaaae12a2adc]
/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe8)[0xffff99713dd8]
/home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres(+0xf2b98)[0xaaaae12a2b98]
Aborted
pg_rewind: error: postgres single-user mode in target cluster failed
pg_rewind: detail: Command was: /home/admin/batta/buildroot/HEAD/pgsql.build/tmp_install/home/admin/batta/buildroot/HEAD/inst/bin/postgres --single -F -D /home/admin/batta/buildroot/HEAD/pgsql.build/src/bin/pg_rewind/tmp_check/t_001_basic_primary_local_data/pgdata -c config_file=/home/admin/batta/buildroot/HEAD/pgsql.build/src/bin/pg_rewind/tmp_check/tmp_test_QbsG/primary-postgresql.conf.tmp template1 < /dev/null
Presumably the reason that batta failed is its special configuration:
shared_preload_libraries = 'pg_stat_statements'; regress_dump_restore; wal_consistency_checking; compute_query_id = regress; --enable-injection-points
Greetings,
Andres Freund
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 21:50 Nathan Bossart <nathandbossart@gmail.com>
parent: Andres Freund <andres@anarazel.de>
0 siblings, 1 reply; 18+ messages in thread
From: Nathan Bossart @ 2026-03-27 21:50 UTC (permalink / raw)
To: Andres Freund <andres@anarazel.de>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Sami Imseih <samimseih@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Fri, Mar 27, 2026 at 05:22:33PM -0400, Andres Freund wrote:
> TRAP: failed Assert("MemoryContextIsValid(context)"), File: "mcxt.c", Line: 1270, PID: 230491
> [...](ExceptionalCondition+0x54)[0xaaaae186c204]
> [...](MemoryContextAllocExtended+0x0)[0xaaaae18a2a24]
> [...](RequestNamedLWLockTranche+0x6c)[0xaaaae16e7310]
> [...](process_shmem_requests+0x28)[0xaaaae1881628]
> [...](PostgresSingleUserMain+0xc4)[0xaaaae1701a34]
> [...](main+0x6ac)[0xaaaae12a2adc]
> /lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe8)[0xffff99713dd8]
> [...](+0xf2b98)[0xaaaae12a2b98]
> Aborted
> pg_rewind: error: postgres single-user mode in target cluster failed
Hm. AFAICT PostmasterContext isn't created in single-user mode, and the
commit in question has RequestNamedLWLockTranche() allocate requests there.
I guess the idea is to allow backends to free that memory after forking
from postmaster, but we don't do that for the NamedLWLockTrancheRequests
list. Maybe we should surround the last part of that function with
MemoryContextSwitchTo(...) to either TopMemoryContext or PostmasterContext
depending on whether we're in single-user mode.
--
nathan
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 22:05 Nathan Bossart <nathandbossart@gmail.com>
parent: Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 18+ messages in thread
From: Nathan Bossart @ 2026-03-27 22:05 UTC (permalink / raw)
To: Andres Freund <andres@anarazel.de>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Sami Imseih <samimseih@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Fri, Mar 27, 2026 at 04:50:12PM -0500, Nathan Bossart wrote:
> On Fri, Mar 27, 2026 at 05:22:33PM -0400, Andres Freund wrote:
>> TRAP: failed Assert("MemoryContextIsValid(context)"), File: "mcxt.c", Line: 1270, PID: 230491
>> [...](ExceptionalCondition+0x54)[0xaaaae186c204]
>> [...](MemoryContextAllocExtended+0x0)[0xaaaae18a2a24]
>> [...](RequestNamedLWLockTranche+0x6c)[0xaaaae16e7310]
>> [...](process_shmem_requests+0x28)[0xaaaae1881628]
>> [...](PostgresSingleUserMain+0xc4)[0xaaaae1701a34]
>> [...](main+0x6ac)[0xaaaae12a2adc]
>> /lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe8)[0xffff99713dd8]
>> [...](+0xf2b98)[0xaaaae12a2b98]
>> Aborted
>> pg_rewind: error: postgres single-user mode in target cluster failed
>
> Hm. AFAICT PostmasterContext isn't created in single-user mode, and the
> commit in question has RequestNamedLWLockTranche() allocate requests there.
> I guess the idea is to allow backends to free that memory after forking
> from postmaster, but we don't do that for the NamedLWLockTrancheRequests
> list. Maybe we should surround the last part of that function with
> MemoryContextSwitchTo(...) to either TopMemoryContext or PostmasterContext
> depending on whether we're in single-user mode.
Concretely, like the attached.
--
nathan
Attachments:
0001-fix-RequestNamedLWLockTranche.patch (text/plain, 1.5K) download
From 714c765399656c9743e3ad76fe52d1cfc8994952 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Fri, 27 Mar 2026 17:00:49 -0500
Subject: [PATCH 1/1] fix RequestNamedLWLockTranche
---
src/backend/storage/lmgr/lwlock.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 7a68071302a..f5b2a6d479d 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -630,6 +630,7 @@ void
RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
{
NamedLWLockTrancheRequest *request;
+ MemoryContext oldcontext;
if (!process_shmem_requests_in_progress)
elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook");
@@ -652,10 +653,17 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
errdetail("No more than %d tranches may be registered.",
MAX_USER_DEFINED_TRANCHES)));
- request = MemoryContextAllocZero(PostmasterContext, sizeof(NamedLWLockTrancheRequest));
+ if (IsPostmasterEnvironment)
+ oldcontext = MemoryContextSwitchTo(PostmasterContext);
+ else
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ request = palloc0(sizeof(NamedLWLockTrancheRequest));
strlcpy(request->tranche_name, tranche_name, NAMEDATALEN);
request->num_lwlocks = num_lwlocks;
NamedLWLockTrancheRequests = lappend(NamedLWLockTrancheRequests, request);
+
+ MemoryContextSwitchTo(oldcontext);
}
/*
--
2.50.1 (Apple Git-155)
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 22:07 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-27 22:07 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; Andres Freund <andres@anarazel.de>; +Cc: Sami Imseih <samimseih@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 28/03/2026 00:05, Nathan Bossart wrote:
> On Fri, Mar 27, 2026 at 04:50:12PM -0500, Nathan Bossart wrote:
>> On Fri, Mar 27, 2026 at 05:22:33PM -0400, Andres Freund wrote:
>>> TRAP: failed Assert("MemoryContextIsValid(context)"), File: "mcxt.c", Line: 1270, PID: 230491
>>> [...](ExceptionalCondition+0x54)[0xaaaae186c204]
>>> [...](MemoryContextAllocExtended+0x0)[0xaaaae18a2a24]
>>> [...](RequestNamedLWLockTranche+0x6c)[0xaaaae16e7310]
>>> [...](process_shmem_requests+0x28)[0xaaaae1881628]
>>> [...](PostgresSingleUserMain+0xc4)[0xaaaae1701a34]
>>> [...](main+0x6ac)[0xaaaae12a2adc]
>>> /lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe8)[0xffff99713dd8]
>>> [...](+0xf2b98)[0xaaaae12a2b98]
>>> Aborted
>>> pg_rewind: error: postgres single-user mode in target cluster failed
>>
>> Hm. AFAICT PostmasterContext isn't created in single-user mode, and the
>> commit in question has RequestNamedLWLockTranche() allocate requests there.
>> I guess the idea is to allow backends to free that memory after forking
>> from postmaster, but we don't do that for the NamedLWLockTrancheRequests
>> list. Maybe we should surround the last part of that function with
>> MemoryContextSwitchTo(...) to either TopMemoryContext or PostmasterContext
>> depending on whether we're in single-user mode.
>
> Concretely, like the attached.
LGTM, thanks! Will you commit or want me to pick it up?
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 22:10 Nathan Bossart <nathandbossart@gmail.com>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Nathan Bossart @ 2026-03-27 22:10 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andres Freund <andres@anarazel.de>; Sami Imseih <samimseih@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Sat, Mar 28, 2026 at 12:07:26AM +0200, Heikki Linnakangas wrote:
> LGTM, thanks! Will you commit or want me to pick it up?
I'm not able to commit it right this second, so feel free to take it. Else
it'll probably be a day or two before I can get to it.
--
nathan
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-27 23:02 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-27 23:02 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Andres Freund <andres@anarazel.de>; Sami Imseih <samimseih@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 28/03/2026 00:10, Nathan Bossart wrote:
> On Sat, Mar 28, 2026 at 12:07:26AM +0200, Heikki Linnakangas wrote:
>> LGTM, thanks! Will you commit or want me to pick it up?
>
> I'm not able to commit it right this second, so feel free to take it. Else
> it'll probably be a day or two before I can get to it.
Ok, committed, thanks!
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-28 17:20 Sami Imseih <samimseih@gmail.com>
parent: Heikki Linnakangas <hlinnaka@iki.fi>
0 siblings, 1 reply; 18+ messages in thread
From: Sami Imseih @ 2026-03-28 17:20 UTC (permalink / raw)
To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Andres Freund <andres@anarazel.de>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Hi Heikki,
Just raising this again to make sure it doesn’t get overlooked [1].
Thanks!
[1] [https://www.postgresql.org/message-id/CAA5RZ0vPWNMvTBqyH7nqDRrHd6Y4Et5iNqXFuwpbsPOk3cL4rQ%40mail.gma...]
--
Sami
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: Clean up NamedLWLockTranche stuff
@ 2026-03-30 14:18 Heikki Linnakangas <hlinnaka@iki.fi>
parent: Sami Imseih <samimseih@gmail.com>
0 siblings, 0 replies; 18+ messages in thread
From: Heikki Linnakangas @ 2026-03-30 14:18 UTC (permalink / raw)
To: Sami Imseih <samimseih@gmail.com>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; Andres Freund <andres@anarazel.de>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 28/03/2026 19:20, Sami Imseih wrote:
> Hi Heikki,
>
> Just raising this again to make sure it doesn’t get overlooked [1].
Fixed, thanks!
- Heikki
^ permalink raw reply [nested|flat] 18+ messages in thread
end of thread, other threads:[~2026-03-30 14:18 UTC | newest]
Thread overview: 18+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-03-26 12:16 Clean up NamedLWLockTranche stuff Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-26 14:37 ` Nathan Bossart <nathandbossart@gmail.com>
2026-03-26 15:21 ` Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-26 16:34 ` Sami Imseih <samimseih@gmail.com>
2026-03-26 16:57 ` Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-26 22:03 ` Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-27 03:57 ` Sami Imseih <samimseih@gmail.com>
2026-03-27 04:49 ` Sami Imseih <samimseih@gmail.com>
2026-03-27 09:45 ` Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-27 14:53 ` Sami Imseih <samimseih@gmail.com>
2026-03-27 21:22 ` Andres Freund <andres@anarazel.de>
2026-03-27 21:50 ` Nathan Bossart <nathandbossart@gmail.com>
2026-03-27 22:05 ` Nathan Bossart <nathandbossart@gmail.com>
2026-03-27 22:07 ` Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-27 22:10 ` Nathan Bossart <nathandbossart@gmail.com>
2026-03-27 23:02 ` Heikki Linnakangas <hlinnaka@iki.fi>
2026-03-28 17:20 ` Sami Imseih <samimseih@gmail.com>
2026-03-30 14:18 ` Heikki Linnakangas <hlinnaka@iki.fi>
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox