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
From 245c4c5086e138f3e65b33b542282fa8563fd0ea Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
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

From c175e2d64ed15811ea4fe8c22a421e31224f49b6 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
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

From f451b44f81e57759a876fc9ca8360826da0537d9 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
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

From d5362cffef3de02259841ce4b049b7bd8898bef9 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
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

From 1c48b2d24460b288fe2c30d42d2cb00ecb5b2a59 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
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

Reply via email to