From ba84c4f086e70a24415d692603ecb9c699773b38 Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-172-31-31-110.ec2.internal>
Date: Thu, 21 Aug 2025 21:26:52 +0000
Subject: [PATCH v12 1/2] Improve LWLock tranche registration

Previously, tranche tracking was done in a backend local array. If a backend
did not explicitly register a tranche with LWLockRegisterTranche, querying
pg_stat_activity would show "extension" instead of the tranche name.

This change reserves more space in NamedLWLockTrancheArray, up to 1024 tranches
with each name of length NAMEDATALEN, in shared memory. Tranche creation is
now centralized. A user only needs to call LWLockNewTrancheId with a tranche
name, and the tranche is stored in shared memory. Access to shared memory is
protected by ShmemLock.

With LWLockNewTrancheId taking full responsibility for registering a tranche,
the LWLockRegisterTranche API is no longer required.

Discussion: https://www.postgresql.org/message-id/CAA5RZ0vvED3naph8My8Szv6DL4AxOVK3eTPS0qXsaKi%3DbVdW2A%40mail.gmail.com
---
 contrib/pg_prewarm/autoprewarm.c              |   3 +-
 doc/src/sgml/xfunc.sgml                       |  18 +-
 src/backend/postmaster/launch_backend.c       |   4 +-
 src/backend/storage/ipc/dsm_registry.c        |  16 +-
 src/backend/storage/lmgr/lwlock.c             | 197 ++++++++----------
 .../utils/activity/wait_event_names.txt       |   1 +
 src/include/storage/lwlock.h                  |  18 +-
 src/include/storage/lwlocklist.h              |   1 +
 src/test/modules/test_dsa/test_dsa.c          |   6 +-
 .../test_dsm_registry/test_dsm_registry.c     |   3 +-
 .../modules/test_radixtree/test_radixtree.c   |   9 +-
 src/test/modules/test_slru/test_slru.c        |   6 +-
 .../modules/test_tidstore/test_tidstore.c     |   3 +-
 13 files changed, 118 insertions(+), 167 deletions(-)

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index c01b9c7e6a4..880e897796a 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -864,7 +864,7 @@ apw_init_state(void *ptr)
 {
 	AutoPrewarmSharedState *state = (AutoPrewarmSharedState *) ptr;
 
-	LWLockInitialize(&state->lock, LWLockNewTrancheId());
+	LWLockInitialize(&state->lock, LWLockNewTrancheId("autoprewarm"));
 	state->bgworker_pid = InvalidPid;
 	state->pid_using_dumpfile = InvalidPid;
 }
@@ -883,7 +883,6 @@ apw_init_shmem(void)
 								   sizeof(AutoPrewarmSharedState),
 								   apw_init_state,
 								   &found);
-	LWLockRegisterTranche(apw_state->lock.tranche, "autoprewarm");
 
 	return found;
 }
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index f116d0648e5..20b0767f204 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3757,9 +3757,10 @@ LWLockPadded *GetNamedLWLockTranche(const char *tranche_name)
       There is another, more flexible method of obtaining LWLocks that can be
       done after server startup and outside a
       <literal>shmem_request_hook</literal>.  To do so, first allocate a
-      <literal>tranche_id</literal> by calling:
+      <literal>tranche_id</literal> with an associated <literal>tranche_name</literal>
+      by calling:
 <programlisting>
-int LWLockNewTrancheId(void)
+int LWLockNewTrancheId(const char *tranche_name)
 </programlisting>
       Next, initialize each LWLock, passing the new
       <literal>tranche_id</literal> as an argument:
@@ -3777,17 +3778,8 @@ void LWLockInitialize(LWLock *lock, int tranche_id)
      </para>
 
      <para>
-      Finally, each backend using the <literal>tranche_id</literal> should
-      associate it with a <literal>tranche_name</literal> by calling:
-<programlisting>
-void LWLockRegisterTranche(int tranche_id, const char *tranche_name)
-</programlisting>
-     </para>
-
-     <para>
-      A complete usage example of <function>LWLockNewTrancheId</function>,
-      <function>LWLockInitialize</function>, and
-      <function>LWLockRegisterTranche</function> can be found in
+      A complete usage example of <function>LWLockNewTrancheId</function> and
+      <function>LWLockInitialize</function> can be found in
       <filename>contrib/pg_prewarm/autoprewarm.c</filename> in the
       <productname>PostgreSQL</productname> source tree.
      </para>
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index bf6b55ee830..51832ed19fe 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -101,8 +101,8 @@ typedef struct
 	struct InjectionPointsCtl *ActiveInjectionPoints;
 #endif
 	int			NamedLWLockTrancheRequests;
-	NamedLWLockTranche *NamedLWLockTrancheArray;
 	LWLockPadded *MainLWLockArray;
+	const char *NamedLWLockTrancheArray;
 	slock_t    *ProcStructLock;
 	PROC_HDR   *ProcGlobal;
 	PGPROC	   *AuxiliaryProcs;
@@ -760,8 +760,8 @@ save_backend_variables(BackendParameters *param,
 #endif
 
 	param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
-	param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
 	param->MainLWLockArray = MainLWLockArray;
+	param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
 	param->ProcStructLock = ProcStructLock;
 	param->ProcGlobal = ProcGlobal;
 	param->AuxiliaryProcs = AuxiliaryProcs;
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1682cc6d34c..8f9595e8730 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -307,9 +307,8 @@ GetNamedDSA(const char *name, bool *found)
 		entry->type = DSMR_ENTRY_TYPE_DSA;
 
 		/* Initialize the LWLock tranche for the DSA. */
-		state->tranche = LWLockNewTrancheId();
+		state->tranche = LWLockNewTrancheId(state->tranche_name);
 		strcpy(state->tranche_name, name);
-		LWLockRegisterTranche(state->tranche, state->tranche_name);
 
 		/* Initialize the DSA. */
 		ret = dsa_create(state->tranche);
@@ -330,9 +329,6 @@ GetNamedDSA(const char *name, bool *found)
 			ereport(ERROR,
 					(errmsg("requested DSA already attached to current process")));
 
-		/* Initialize existing LWLock tranche for the DSA. */
-		LWLockRegisterTranche(state->tranche, state->tranche_name);
-
 		/* Attach to existing DSA. */
 		ret = dsa_attach(state->handle);
 		dsa_pin_mapping(ret);
@@ -389,14 +385,12 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
 		entry->type = DSMR_ENTRY_TYPE_DSH;
 
 		/* Initialize the LWLock tranche for the DSA. */
-		dsa_state->tranche = LWLockNewTrancheId();
+		dsa_state->tranche = LWLockNewTrancheId(dsa_state->tranche_name);
 		sprintf(dsa_state->tranche_name, "%s%s", name, DSMR_DSA_TRANCHE_SUFFIX);
-		LWLockRegisterTranche(dsa_state->tranche, dsa_state->tranche_name);
 
 		/* Initialize the LWLock tranche for the dshash table. */
-		dsh_state->tranche = LWLockNewTrancheId();
+		dsh_state->tranche = LWLockNewTrancheId(dsh_state->tranche_name);
 		strcpy(dsh_state->tranche_name, name);
-		LWLockRegisterTranche(dsh_state->tranche, dsh_state->tranche_name);
 
 		/* Initialize the DSA for the hash table. */
 		dsa = dsa_create(dsa_state->tranche);
@@ -427,10 +421,6 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
 			ereport(ERROR,
 					(errmsg("requested DSHash already attached to current process")));
 
-		/* Initialize existing LWLock tranches for the DSA and dshash table. */
-		LWLockRegisterTranche(dsa_state->tranche, dsa_state->tranche_name);
-		LWLockRegisterTranche(dsh_state->tranche, dsh_state->tranche_name);
-
 		/* Attach to existing DSA for the hash table. */
 		dsa = dsa_attach(dsa_state->handle);
 		dsa_pin_mapping(dsa);
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index c80b43f1f55..e6ab1070bda 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -105,6 +105,9 @@
 #define LW_SHARED_MASK				MAX_BACKENDS
 #define LW_LOCK_MASK				(MAX_BACKENDS | LW_VAL_EXCLUSIVE)
 
+#define MAX_NAMED_TRANCHES			1024
+#define MAX_NAMED_TRANCHES_NAME_LEN			NAMEDATALEN
+
 
 StaticAssertDecl(((MAX_BACKENDS + 1) & MAX_BACKENDS) == 0,
 				 "MAX_BACKENDS + 1 needs to be a power of 2");
@@ -125,9 +128,9 @@ StaticAssertDecl((LW_VAL_EXCLUSIVE & LW_FLAG_MASK) == 0,
  * 2. There are some predefined tranches for built-in groups of locks defined
  * in lwlocklist.h.  We absorb the names of these tranches, too.
  *
- * 3. Extensions can create new tranches, via either RequestNamedLWLockTranche
- * or LWLockRegisterTranche.  The names of these that are known in the current
- * process appear in LWLockTrancheNames[].
+ * 3. Extensions can create new tranches using either RequestNamedLWLockTranche
+ * or LWLockNewTrancheId. The names of these tranches are stored in the shared
+ * NamedLWLockTrancheArray.
  *
  * 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.
@@ -146,11 +149,12 @@ StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
 
 /*
  * This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED, and
- * stores the names of all dynamically-created tranches known to the current
- * process.  Any unused entries in the array will contain NULL.
+ * stores the names of all dynamically created tranches in shared memory.
+ * Any unused entries in the array will contain NULL. This variable is
+ * non-static so that postmaster.c can copy it to child processes in
+ * EXEC_BACKEND builds.
  */
-static const char **LWLockTrancheNames = NULL;
-static int	LWLockTrancheNamesAllocated = 0;
+const char *NamedLWLockTrancheArray = NULL;
 
 /*
  * This points to the main array of LWLocks in shared memory.  Backends inherit
@@ -179,12 +183,11 @@ static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS];
 /* struct representing the LWLock tranche request for named tranche */
 typedef struct NamedLWLockTrancheRequest
 {
-	char		tranche_name[NAMEDATALEN];
+	char		tranche_name[MAX_NAMED_TRANCHES_NAME_LEN];
 	int			num_lwlocks;
 } NamedLWLockTrancheRequest;
 
 static NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
-static int	NamedLWLockTrancheRequestsAllocated = 0;
 
 /*
  * NamedLWLockTrancheRequests is both the valid length of the request array,
@@ -194,8 +197,13 @@ static int	NamedLWLockTrancheRequestsAllocated = 0;
  */
 int			NamedLWLockTrancheRequests = 0;
 
-/* points to data in shared memory: */
-NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
+/*
+ * Backend-local counter for the number of registered tranches.
+ * This is used to ensure the backend is aware of the latest tranche
+ * before accessing shared memory.
+ */
+static int	NamedLWLocksCount = 0;
+
 
 static void InitializeLWLocks(void);
 static inline void LWLockReportWaitStart(LWLock *lock);
@@ -391,7 +399,6 @@ Size
 LWLockShmemSize(void)
 {
 	Size		size;
-	int			i;
 	int			numLocks = NUM_FIXED_LWLOCKS;
 
 	/* Calculate total number of locks needed in the main array. */
@@ -403,19 +410,18 @@ LWLockShmemSize(void)
 	/* Space for dynamic allocation counter, plus room for alignment. */
 	size = add_size(size, sizeof(int) + LWLOCK_PADDED_SIZE);
 
-	/* space for named tranches. */
+	/* Space for named tranches. */
 	size = add_size(size, mul_size(NamedLWLockTrancheRequests, sizeof(NamedLWLockTranche)));
 
-	/* space for name of each tranche. */
-	for (i = 0; i < NamedLWLockTrancheRequests; i++)
-		size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);
+	/* Space for name of each tranche. */
+	size = add_size(size, mul_size(MAX_NAMED_TRANCHES, MAX_NAMED_TRANCHES));
 
 	return size;
 }
 
 /*
  * Allocate shmem space for the main LWLock array and all tranches and
- * initialize it.  We also register extension LWLock tranches here.
+ * initialize it.
  */
 void
 CreateLWLocks(void)
@@ -447,11 +453,6 @@ CreateLWLocks(void)
 		/* Initialize all LWLocks */
 		InitializeLWLocks();
 	}
-
-	/* Register named extension LWLock tranches in the current process. */
-	for (int i = 0; i < NamedLWLockTrancheRequests; i++)
-		LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
-							  NamedLWLockTrancheArray[i].trancheName);
 }
 
 /*
@@ -485,38 +486,27 @@ InitializeLWLocks(void)
 	for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++)
 		LWLockInitialize(&lock->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.
-	 */
+	NamedLWLockTrancheArray = (const char *)
+		&MainLWLockArray[NUM_FIXED_LWLOCKS + numNamedLocks];
+
+	/* Register LWLocks requested with RequestNamedLWLockTranch */
 	if (NamedLWLockTrancheRequests > 0)
 	{
-		char	   *trancheNames;
-
-		NamedLWLockTrancheArray = (NamedLWLockTranche *)
-			&MainLWLockArray[NUM_FIXED_LWLOCKS + numNamedLocks];
-
-		trancheNames = (char *) NamedLWLockTrancheArray +
-			(NamedLWLockTrancheRequests * sizeof(NamedLWLockTranche));
 		lock = &MainLWLockArray[NUM_FIXED_LWLOCKS];
 
 		for (i = 0; i < NamedLWLockTrancheRequests; i++)
 		{
 			NamedLWLockTrancheRequest *request;
-			NamedLWLockTranche *tranche;
-			char	   *name;
+			int			tranche_id;
 
 			request = &NamedLWLockTrancheRequestArray[i];
-			tranche = &NamedLWLockTrancheArray[i];
 
-			name = trancheNames;
-			trancheNames += strlen(request->tranche_name) + 1;
-			strcpy(name, request->tranche_name);
-			tranche->trancheId = LWLockNewTrancheId();
-			tranche->trancheName = name;
+			/* Allocate a new tranche ID */
+			tranche_id = LWLockNewTrancheId(request->tranche_name);
 
+			/* Initialize the requested amount of LWLocks */
 			for (j = 0; j < request->num_lwlocks; j++, lock++)
-				LWLockInitialize(&lock->lock, tranche->trancheId);
+				LWLockInitialize(&lock->lock, tranche_id);
 		}
 	}
 }
@@ -571,58 +561,36 @@ GetNamedLWLockTranche(const char *tranche_name)
  * Allocate a new tranche ID.
  */
 int
-LWLockNewTrancheId(void)
+LWLockNewTrancheId(const char *tranche_name)
 {
-	int			result;
-	int		   *LWLockCounter;
+	int			tranche_id,
+				index,
+			   *LWLockCounter;
+	Size		tranche_name_length = strlen(tranche_name) + 1;
 
-	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
-	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	if (tranche_name_length > MAX_NAMED_TRANCHES_NAME_LEN)
+		elog(ERROR, "tranche name too long");
 
-	return result;
-}
+	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 
-/*
- * Register a dynamic tranche name in the lookup table of the current process.
- *
- * This routine will save a pointer to the tranche name passed as an argument,
- * so the name should be allocated in a backend-lifetime context
- * (shared memory, TopMemoryContext, static constant, or similar).
- *
- * The tranche name will be user-visible as a wait event name, so try to
- * use a name that fits the style for those.
- */
-void
-LWLockRegisterTranche(int tranche_id, const char *tranche_name)
-{
-	/* This should only be called for user-defined tranches. */
-	if (tranche_id < LWTRANCHE_FIRST_USER_DEFINED)
-		return;
+	SpinLockAcquire(ShmemLock);
 
-	/* Convert to array index. */
-	tranche_id -= LWTRANCHE_FIRST_USER_DEFINED;
+	tranche_id = (*LWLockCounter)++;
+	index = tranche_id - LWTRANCHE_FIRST_USER_DEFINED;
 
-	/* If necessary, create or enlarge array. */
-	if (tranche_id >= LWLockTrancheNamesAllocated)
+	if (index >= MAX_NAMED_TRANCHES)
 	{
-		int			newalloc;
+		SpinLockRelease(ShmemLock);
+		elog(ERROR, "too many LWLock tranches registered");
+	}
 
-		newalloc = pg_nextpower2_32(Max(8, tranche_id + 1));
+	/* Directly copy into the NamedLWLockTrancheArray */
+	strcpy((char *) NamedLWLockTrancheArray + (index * MAX_NAMED_TRANCHES_NAME_LEN),
+		   tranche_name);
 
-		if (LWLockTrancheNames == NULL)
-			LWLockTrancheNames = (const char **)
-				MemoryContextAllocZero(TopMemoryContext,
-									   newalloc * sizeof(char *));
-		else
-			LWLockTrancheNames =
-				repalloc0_array(LWLockTrancheNames, const char *, LWLockTrancheNamesAllocated, newalloc);
-		LWLockTrancheNamesAllocated = newalloc;
-	}
+	SpinLockRelease(ShmemLock);
 
-	LWLockTrancheNames[tranche_id] = tranche_name;
+	return tranche_id;
 }
 
 /*
@@ -641,42 +609,41 @@ void
 RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
 {
 	NamedLWLockTrancheRequest *request;
+	Size		tranche_name_length = strlen(tranche_name) + 1;
 
 	if (!process_shmem_requests_in_progress)
 		elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook");
 
+	if (tranche_name_length > MAX_NAMED_TRANCHES_NAME_LEN)
+		elog(FATAL, "tranche name too long");
+
 	if (NamedLWLockTrancheRequestArray == NULL)
 	{
-		NamedLWLockTrancheRequestsAllocated = 16;
 		NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
 			MemoryContextAlloc(TopMemoryContext,
-							   NamedLWLockTrancheRequestsAllocated
+							   MAX_NAMED_TRANCHES
 							   * sizeof(NamedLWLockTrancheRequest));
 	}
 
-	if (NamedLWLockTrancheRequests >= NamedLWLockTrancheRequestsAllocated)
-	{
-		int			i = pg_nextpower2_32(NamedLWLockTrancheRequests + 1);
-
-		NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
-			repalloc(NamedLWLockTrancheRequestArray,
-					 i * sizeof(NamedLWLockTrancheRequest));
-		NamedLWLockTrancheRequestsAllocated = i;
-	}
+	if (NamedLWLockTrancheRequests >= MAX_NAMED_TRANCHES)
+		elog(FATAL, "too many LWLock tranches registered");
 
 	request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests];
-	Assert(strlen(tranche_name) + 1 <= NAMEDATALEN);
-	strlcpy(request->tranche_name, tranche_name, NAMEDATALEN);
+	strlcpy(request->tranche_name, tranche_name, tranche_name_length);
 	request->num_lwlocks = num_lwlocks;
 	NamedLWLockTrancheRequests++;
 }
 
 /*
  * LWLockInitialize - initialize a new lwlock; it's initially unlocked
+ * If the tranche was never registered, GetLWTrancheName will return
+ * and error.
  */
 void
 LWLockInitialize(LWLock *lock, int tranche_id)
 {
+	GetLWTrancheName(tranche_id);
+
 	pg_atomic_init_u32(&lock->state, LW_FLAG_RELEASE_OK);
 #ifdef LOCK_DEBUG
 	pg_atomic_init_u32(&lock->nwaiters, 0);
@@ -713,22 +680,40 @@ LWLockReportWaitEnd(void)
 static const char *
 GetLWTrancheName(uint16 trancheId)
 {
-	/* Built-in tranche or individual LWLock? */
+	int			index;
+
+	/* Built-in tranche? */
 	if (trancheId < LWTRANCHE_FIRST_USER_DEFINED)
 		return BuiltinTrancheNames[trancheId];
 
 	/*
-	 * It's an extension tranche, so look in LWLockTrancheNames[].  However,
-	 * it's possible that the tranche has never been registered in the current
-	 * process, in which case give up and return "extension".
+	 * It's an extension tranche, so look in NamedLWLockTrancheArray[].
+	 * Compute the index to lookup NamedLWLockTrancheArray[].
 	 */
-	trancheId -= LWTRANCHE_FIRST_USER_DEFINED;
+	index = trancheId - LWTRANCHE_FIRST_USER_DEFINED;
 
-	if (trancheId >= LWLockTrancheNamesAllocated ||
-		LWLockTrancheNames[trancheId] == NULL)
-		return "extension";
+	/*
+	 * If the requested index exceeds the count of known tranches, update
+	 * NamedLWLocksCount to reflect the current state in shared memory. This
+	 * ensures the backend is aware of all registered tranches.
+	 */
+	if (index >= NamedLWLocksCount)
+	{
+		int		   *LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 
-	return LWLockTrancheNames[trancheId];
+		SpinLockAcquire(ShmemLock);
+		NamedLWLocksCount = *LWLockCounter - LWTRANCHE_FIRST_USER_DEFINED;
+		SpinLockRelease(ShmemLock);
+	}
+
+	/*
+	 * Return the tranche name if the index is valid. Otherwise, raise an
+	 * error.
+	 */
+	if (index < NamedLWLocksCount)
+		return NamedLWLockTrancheArray + (index * MAX_NAMED_TRANCHES_NAME_LEN);
+	else
+		elog(ERROR, "LWLock tranche is not registered");
 }
 
 /*
diff --git a/src/backend/utils/activity/wait_event_names.txt b/src/backend/utils/activity/wait_event_names.txt
index 5427da5bc1b..2eed0a0682d 100644
--- a/src/backend/utils/activity/wait_event_names.txt
+++ b/src/backend/utils/activity/wait_event_names.txt
@@ -352,6 +352,7 @@ DSMRegistry	"Waiting to read or update the dynamic shared memory registry."
 InjectionPoint	"Waiting to read or update information related to injection points."
 SerialControl	"Waiting to read or update shared <filename>pg_serial</filename> state."
 AioWorkerSubmissionQueue	"Waiting to access AIO worker submission queue."
+NamedLWLockTrancheArray	"Waiting to access the named LWLock tranches array."
 
 #
 # END OF PREDEFINED LWLOCKS (DO NOT CHANGE THIS LINE)
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 5e717765764..1ba77cb3658 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -80,7 +80,7 @@ typedef struct NamedLWLockTranche
 	char	   *trancheName;
 } NamedLWLockTranche;
 
-extern PGDLLIMPORT NamedLWLockTranche *NamedLWLockTrancheArray;
+extern PGDLLIMPORT const char *NamedLWLockTrancheArray;
 extern PGDLLIMPORT int NamedLWLockTrancheRequests;
 
 /*
@@ -157,19 +157,11 @@ extern LWLockPadded *GetNamedLWLockTranche(const char *tranche_name);
 
 /*
  * There is another, more flexible method of obtaining lwlocks. First, call
- * LWLockNewTrancheId just once to obtain a tranche ID; this allocates from
- * a shared counter.  Next, each individual process using the tranche should
- * call LWLockRegisterTranche() to associate that tranche ID with a name.
- * Finally, LWLockInitialize should be called just once per lwlock, passing
- * the tranche ID as an argument.
- *
- * It may seem strange that each process using the tranche must register it
- * separately, but dynamic shared memory segments aren't guaranteed to be
- * mapped at the same address in all coordinating backends, so storing the
- * registration in the main shared memory segment wouldn't work for that case.
+ * LWLockNewTrancheId with an associated tranche name just once to obtain a
+ * tranche ID; this allocates from a shared counter. Finally, LWLockInitialize
+ * should be called just once per lwlock, passing the tranche ID as an argument.
  */
-extern int	LWLockNewTrancheId(void);
-extern void LWLockRegisterTranche(int tranche_id, const char *tranche_name);
+extern int	LWLockNewTrancheId(const char *tranche_name);
 extern void LWLockInitialize(LWLock *lock, int tranche_id);
 
 /*
diff --git a/src/include/storage/lwlocklist.h b/src/include/storage/lwlocklist.h
index 06a1ffd4b08..0b00c5a4b3f 100644
--- a/src/include/storage/lwlocklist.h
+++ b/src/include/storage/lwlocklist.h
@@ -85,6 +85,7 @@ PG_LWLOCK(50, DSMRegistry)
 PG_LWLOCK(51, InjectionPoint)
 PG_LWLOCK(52, SerialControl)
 PG_LWLOCK(53, AioWorkerSubmissionQueue)
+PG_LWLOCK(54, NamedLWLockTrancheArray)
 
 /*
  * There also exist several built-in LWLock tranches.  As with the predefined
diff --git a/src/test/modules/test_dsa/test_dsa.c b/src/test/modules/test_dsa/test_dsa.c
index cd24d0f4873..01d5c6fa67f 100644
--- a/src/test/modules/test_dsa/test_dsa.c
+++ b/src/test/modules/test_dsa/test_dsa.c
@@ -29,8 +29,7 @@ test_dsa_basic(PG_FUNCTION_ARGS)
 	dsa_pointer p[100];
 
 	/* XXX: this tranche is leaked */
-	tranche_id = LWLockNewTrancheId();
-	LWLockRegisterTranche(tranche_id, "test_dsa");
+	tranche_id = LWLockNewTrancheId("test_dsa");
 
 	a = dsa_create(tranche_id);
 	for (int i = 0; i < 100; i++)
@@ -70,8 +69,7 @@ test_dsa_resowners(PG_FUNCTION_ARGS)
 	ResourceOwner childowner;
 
 	/* XXX: this tranche is leaked */
-	tranche_id = LWLockNewTrancheId();
-	LWLockRegisterTranche(tranche_id, "test_dsa");
+	tranche_id = LWLockNewTrancheId("test_dsa");
 
 	/* Create DSA in parent resource owner */
 	a = dsa_create(tranche_id);
diff --git a/src/test/modules/test_dsm_registry/test_dsm_registry.c b/src/test/modules/test_dsm_registry/test_dsm_registry.c
index 141c8ed1b34..4cc2ccdac3f 100644
--- a/src/test/modules/test_dsm_registry/test_dsm_registry.c
+++ b/src/test/modules/test_dsm_registry/test_dsm_registry.c
@@ -48,7 +48,7 @@ init_tdr_dsm(void *ptr)
 {
 	TestDSMRegistryStruct *dsm = (TestDSMRegistryStruct *) ptr;
 
-	LWLockInitialize(&dsm->lck, LWLockNewTrancheId());
+	LWLockInitialize(&dsm->lck, LWLockNewTrancheId("test_dsm_registry"));
 	dsm->val = 0;
 }
 
@@ -61,7 +61,6 @@ tdr_attach_shmem(void)
 								 sizeof(TestDSMRegistryStruct),
 								 init_tdr_dsm,
 								 &found);
-	LWLockRegisterTranche(tdr_dsm->lck.tranche, "test_dsm_registry");
 
 	if (tdr_dsa == NULL)
 		tdr_dsa = GetNamedDSA("test_dsm_registry_dsa", &found);
diff --git a/src/test/modules/test_radixtree/test_radixtree.c b/src/test/modules/test_radixtree/test_radixtree.c
index 80ad0296164..787162c8793 100644
--- a/src/test/modules/test_radixtree/test_radixtree.c
+++ b/src/test/modules/test_radixtree/test_radixtree.c
@@ -124,10 +124,9 @@ test_empty(void)
 	rt_iter    *iter;
 	uint64		key;
 #ifdef TEST_SHARED_RT
-	int			tranche_id = LWLockNewTrancheId();
+	int			tranche_id = LWLockNewTrancheId("test_radix_tree");
 	dsa_area   *dsa;
 
-	LWLockRegisterTranche(tranche_id, "test_radix_tree");
 	dsa = dsa_create(tranche_id);
 	radixtree = rt_create(dsa, tranche_id);
 #else
@@ -167,10 +166,9 @@ test_basic(rt_node_class_test_elem *test_info, int shift, bool asc)
 	uint64	   *keys;
 	int			children = test_info->nkeys;
 #ifdef TEST_SHARED_RT
-	int			tranche_id = LWLockNewTrancheId();
+	int			tranche_id = LWLockNewTrancheId("test_radix_tree");
 	dsa_area   *dsa;
 
-	LWLockRegisterTranche(tranche_id, "test_radix_tree");
 	dsa = dsa_create(tranche_id);
 	radixtree = rt_create(dsa, tranche_id);
 #else
@@ -304,10 +302,9 @@ test_random(void)
 	int			num_keys = 100000;
 	uint64	   *keys;
 #ifdef TEST_SHARED_RT
-	int			tranche_id = LWLockNewTrancheId();
+	int			tranche_id = LWLockNewTrancheId("test_radix_tree");
 	dsa_area   *dsa;
 
-	LWLockRegisterTranche(tranche_id, "test_radix_tree");
 	dsa = dsa_create(tranche_id);
 	radixtree = rt_create(dsa, tranche_id);
 #else
diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c
index 32750930e43..8c0367eeee4 100644
--- a/src/test/modules/test_slru/test_slru.c
+++ b/src/test/modules/test_slru/test_slru.c
@@ -232,11 +232,9 @@ test_slru_shmem_startup(void)
 	(void) MakePGDirectory(slru_dir_name);
 
 	/* initialize the SLRU facility */
-	test_tranche_id = LWLockNewTrancheId();
-	LWLockRegisterTranche(test_tranche_id, "test_slru_tranche");
+	test_tranche_id = LWLockNewTrancheId("test_slru_tranche");
 
-	test_buffer_tranche_id = LWLockNewTrancheId();
-	LWLockRegisterTranche(test_tranche_id, "test_buffer_tranche");
+	test_buffer_tranche_id = LWLockNewTrancheId("test_buffer_tranche");
 
 	TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
 	SimpleLruInit(TestSlruCtl, "TestSLRU",
diff --git a/src/test/modules/test_tidstore/test_tidstore.c b/src/test/modules/test_tidstore/test_tidstore.c
index eb16e0fbfa6..0c8f43867e5 100644
--- a/src/test/modules/test_tidstore/test_tidstore.c
+++ b/src/test/modules/test_tidstore/test_tidstore.c
@@ -103,8 +103,7 @@ test_create(PG_FUNCTION_ARGS)
 	{
 		int			tranche_id;
 
-		tranche_id = LWLockNewTrancheId();
-		LWLockRegisterTranche(tranche_id, "test_tidstore");
+		tranche_id = LWLockNewTrancheId("test_tidstore");
 
 		tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id);
 
-- 
2.39.5 (Apple Git-154)

