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

