From c06e8ef0599bda5f2fe77c46087a768c47ea23d4 Mon Sep 17 00:00:00 2001
From: Sami Imseih <simseih@amazon.com>
Date: Mon, 21 Jul 2025 19:02:47 -0500
Subject: [PATCH v3 1/1] Store LWLock tranche names registered after postmaster
 startup in dynamic shared memory.

---
 contrib/pg_prewarm/autoprewarm.c              |   4 +-
 doc/src/sgml/xfunc.sgml                       |  18 +-
 src/backend/storage/ipc/dsm_registry.c        |  16 +-
 src/backend/storage/ipc/ipci.c                |   2 +
 src/backend/storage/lmgr/lwlock.c             | 324 +++++++++++++++---
 .../utils/activity/wait_event_names.txt       |   2 +
 src/backend/utils/init/postinit.c             |   2 +
 src/include/storage/lwlock.h                  |  22 +-
 src/include/storage/lwlocklist.h              |   2 +
 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 +-
 14 files changed, 312 insertions(+), 107 deletions(-)

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index c01b9c7e6a4..b623589b430 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,8 +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 2d81afce8cb..a45d3039b47 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3758,9 +3758,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:
@@ -3778,17 +3779,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/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1682cc6d34c..cc0adc19971 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();
 		strcpy(state->tranche_name, name);
-		LWLockRegisterTranche(state->tranche, state->tranche_name);
+		state->tranche = LWLockNewTrancheId(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();
 		sprintf(dsa_state->tranche_name, "%s%s", name, DSMR_DSA_TRANCHE_SUFFIX);
-		LWLockRegisterTranche(dsa_state->tranche, dsa_state->tranche_name);
+		dsa_state->tranche = LWLockNewTrancheId(dsa_state->tranche_name);
 
 		/* Initialize the LWLock tranche for the dshash table. */
-		dsh_state->tranche = LWLockNewTrancheId();
 		strcpy(dsh_state->tranche_name, name);
-		LWLockRegisterTranche(dsh_state->tranche, dsh_state->tranche_name);
+		dsh_state->tranche = LWLockNewTrancheId(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/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..c8e4d55cf80 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -343,6 +343,8 @@ CreateOrAttachShmemStructs(void)
 	WaitEventCustomShmemInit();
 	InjectionPointShmemInit();
 	AioShmemInit();
+
+	LWLockTrancheNamesShmemInit();
 }
 
 /*
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index ec9c345ffdf..e777d23d098 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,7 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "storage/ipc.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -126,8 +127,9 @@ 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 LWLockRegisterTranche.  The names of these that are known in the current
- * process appear in LWLockTrancheNames[].
+ * or LWLockNewTrancheId.  The names of these are registered in LWLockTrancheNames
+ * either by during postmaster startup or by after. For the latter case,
+ * there is a dedicated dynamic shared memory to store the tranche named.
  *
  * 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.
@@ -144,14 +146,6 @@ StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
 				 LWTRANCHE_FIRST_USER_DEFINED,
 				 "missing entries in 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.
- */
-static const char **LWLockTrancheNames = NULL;
-static int	LWLockTrancheNamesAllocated = 0;
-
 /*
  * 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,
@@ -187,6 +181,45 @@ typedef struct NamedLWLockTrancheRequest
 static NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
 static int	NamedLWLockTrancheRequestsAllocated = 0;
 
+typedef struct LWLockTrancheNamesShmem
+{
+	/* pointer to the DSA shared memory, created in-place */
+	void	   *raw_dsa_area;
+	/* DSA Pointer to a list of DSA pointers, each storing a tranche name */
+	dsa_pointer list_ptr;
+	/* number of tranche names stored in shared memory */
+	int			count;
+
+	/*
+	 * The number of slots allocated for tranche names. The allocated size
+	 * will grow geometrically to reduce resizing of the list_ptr.
+	 */
+	int			allocated;
+	/* lock to protect access to the list */
+	LWLock		lock;
+}			LWLockTrancheNamesShmem;
+
+typedef struct LWLockTrancheNamesStruct
+{
+	/* Fields for tranche names reated during ostmaster startup */
+	const char **local;
+	int			allocated;
+
+	/*
+	 * Fields for shared memory tranche names ( those created after postmaster
+	 * startup )
+	 */
+	LWLockTrancheNamesShmem *shmem;
+	dsa_area   *dsa;
+}			LWLockTrancheNamesStruct;
+
+static LWLockTrancheNamesStruct LWLockTrancheNames =
+{
+	NULL, 0, NULL, NULL
+};
+
+#define LWLOCK_TRANCHE_NAMES_INIT_SIZE 16
+
 /*
  * NamedLWLockTrancheRequests is both the valid length of the request array,
  * and the length of the shared-memory NamedLWLockTrancheArray later on.
@@ -202,6 +235,10 @@ static void InitializeLWLocks(void);
 static inline void LWLockReportWaitStart(LWLock *lock);
 static inline void LWLockReportWaitEnd(void);
 static const char *GetLWTrancheName(uint16 trancheId);
+static void LWLockRegisterTranche(int tranche_id,
+								  const char *tranche_name);
+static void LWLockTrancheNamesAttach(void);
+static void LWLockTrancheNamesDetach(void);
 
 #define T_NAME(lock) \
 	GetLWTrancheName((lock)->tranche)
@@ -447,12 +484,12 @@ 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);
+		/* Register named extension LWLock tranches in the current process. */
+		for (int i = 0; i < NamedLWLockTrancheRequests; i++)
+			LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
+								  NamedLWLockTrancheArray[i].trancheName);
+	}
 }
 
 /*
@@ -513,7 +550,7 @@ InitializeLWLocks(void)
 			name = trancheNames;
 			trancheNames += strlen(request->tranche_name) + 1;
 			strcpy(name, request->tranche_name);
-			tranche->trancheId = LWLockNewTrancheId();
+			tranche->trancheId = LWLockNewTrancheId(name);
 			tranche->trancheName = name;
 
 			for (j = 0; j < request->num_lwlocks; j++, lock++)
@@ -569,21 +606,30 @@ GetNamedLWLockTranche(const char *tranche_name)
 }
 
 /*
- * Allocate a new tranche ID.
+ * Allocate a new tranche ID and register it with the associated tranche
+ * name.
+ *
+ * The tranche name will be user-visible as a wait event name, so try to
+ * use a name that fits the style for those.
  */
 int
-LWLockNewTrancheId(void)
+LWLockNewTrancheId(const char *tranche_name)
 {
-	int			result;
+	int			tranche_id;
+	int			tranche_index;
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
 	SpinLockAcquire(ShmemLock);
-	result = (*LWLockCounter)++;
+	tranche_id = (*LWLockCounter)++;
 	SpinLockRelease(ShmemLock);
 
-	return result;
+	tranche_index = tranche_id - LWTRANCHE_FIRST_USER_DEFINED;
+
+	LWLockRegisterTranche(tranche_index, tranche_name);
+
+	return tranche_id;
 }
 
 /*
@@ -593,37 +639,79 @@ LWLockNewTrancheId(void)
  * 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
+static 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;
-
-	/* Convert to array index. */
-	tranche_id -= LWTRANCHE_FIRST_USER_DEFINED;
+	int			newalloc;
 
-	/* If necessary, create or enlarge array. */
-	if (tranche_id >= LWLockTrancheNamesAllocated)
+	if (!IsUnderPostmaster)
 	{
-		int			newalloc;
+		/* If necessary, create or enlarge array. */
+		if (tranche_id >= LWLockTrancheNames.allocated)
+		{
+			newalloc = pg_nextpower2_32(Max(8, tranche_id + 1));
 
-		newalloc = pg_nextpower2_32(Max(8, tranche_id + 1));
+			if (LWLockTrancheNames.local == NULL)
+				LWLockTrancheNames.local = (const char **)
+					MemoryContextAllocZero(TopMemoryContext,
+										   newalloc * sizeof(char *));
+			else
+				LWLockTrancheNames.local =
+					repalloc0_array(LWLockTrancheNames.local, const char *, LWLockTrancheNames.allocated, newalloc);
+			LWLockTrancheNames.allocated = newalloc;
+		}
 
-		if (LWLockTrancheNames == NULL)
-			LWLockTrancheNames = (const char **)
-				MemoryContextAllocZero(TopMemoryContext,
-									   newalloc * sizeof(char *));
-		else
-			LWLockTrancheNames =
-				repalloc0_array(LWLockTrancheNames, const char *, LWLockTrancheNamesAllocated, newalloc);
-		LWLockTrancheNamesAllocated = newalloc;
+		LWLockTrancheNames.local[tranche_id] = tranche_name;
 	}
+	else
+	{
+		dsa_pointer *current_ptrs;
+		dsa_pointer new_list_ptr;
+		dsa_pointer *new_name_ptrs;
+		size_t		len;
+		dsa_pointer str_ptr;
+		char	   *str_addr;
+
+		LWLockAcquire(&LWLockTrancheNames.shmem->lock, LW_EXCLUSIVE);
+
+		/*
+		 * The first time adding a name to the shmem, so let's allocate the
+		 * list now.
+		 */
+		if (LWLockTrancheNames.shmem->list_ptr == InvalidDsaPointer)
+		{
+			LWLockTrancheNames.shmem->list_ptr = dsa_allocate(LWLockTrancheNames.dsa,
+															  LWLOCK_TRANCHE_NAMES_INIT_SIZE * sizeof(dsa_pointer));
+			LWLockTrancheNames.shmem->allocated = LWLOCK_TRANCHE_NAMES_INIT_SIZE;
+		}
+
+		current_ptrs = dsa_get_address(LWLockTrancheNames.dsa, LWLockTrancheNames.shmem->list_ptr);
+
+		/* Resize if needed */
+		if (LWLockTrancheNames.shmem->count >= LWLockTrancheNames.shmem->allocated)
+		{
+			newalloc = pg_nextpower2_32(Max(LWLOCK_TRANCHE_NAMES_INIT_SIZE, LWLockTrancheNames.shmem->allocated + 1));
+			new_list_ptr = dsa_allocate(LWLockTrancheNames.dsa, newalloc * sizeof(dsa_pointer));
+			new_name_ptrs = dsa_get_address(LWLockTrancheNames.dsa, new_list_ptr);
+			memcpy(new_name_ptrs, current_ptrs, LWLockTrancheNames.shmem->allocated * sizeof(dsa_pointer));
+			dsa_free(LWLockTrancheNames.dsa, *current_ptrs);
+			LWLockTrancheNames.shmem->list_ptr = new_list_ptr;
+			LWLockTrancheNames.shmem->allocated = newalloc;
+			current_ptrs = new_name_ptrs;
+		}
+
+		/* Allocate and copy string */
+		len = strlen(tranche_name) + 1;
+		str_ptr = dsa_allocate(LWLockTrancheNames.dsa, len);
+		str_addr = dsa_get_address(LWLockTrancheNames.dsa, str_ptr);
+		memcpy(str_addr, tranche_name, len);
 
-	LWLockTrancheNames[tranche_id] = tranche_name;
+		current_ptrs[tranche_id] = str_ptr;
+		LWLockTrancheNames.shmem->count++;
+
+		LWLockRelease(&LWLockTrancheNames.shmem->lock);
+	}
 }
 
 /*
@@ -708,12 +796,32 @@ LWLockReportWaitEnd(void)
 	pgstat_report_wait_end();
 }
 
+
+static const char *
+GetLWTrancheNameByID(int tranche_id)
+{
+	dsa_pointer *pointers;
+	char	   *str = NULL;
+
+	LWLockAcquire(&LWLockTrancheNames.shmem->lock, LW_SHARED);
+
+	pointers = dsa_get_address(LWLockTrancheNames.dsa, LWLockTrancheNames.shmem->list_ptr);
+
+	str = dsa_get_address(LWLockTrancheNames.dsa, pointers[tranche_id]);
+
+	LWLockRelease(&LWLockTrancheNames.shmem->lock);
+
+	return str;
+}
+
 /*
  * Return the name of an LWLock tranche.
  */
 static const char *
 GetLWTrancheName(uint16 trancheId)
 {
+	const char *tranche_name = NULL;
+
 	/* Built-in tranche or individual LWLock? */
 	if (trancheId < LWTRANCHE_FIRST_USER_DEFINED)
 		return BuiltinTrancheNames[trancheId];
@@ -725,11 +833,15 @@ GetLWTrancheName(uint16 trancheId)
 	 */
 	trancheId -= LWTRANCHE_FIRST_USER_DEFINED;
 
-	if (trancheId >= LWLockTrancheNamesAllocated ||
-		LWLockTrancheNames[trancheId] == NULL)
-		return "extension";
+	if (trancheId < LWLockTrancheNames.allocated &&
+		LWLockTrancheNames.local[trancheId] != NULL)
+		return LWLockTrancheNames.local[trancheId];
 
-	return LWLockTrancheNames[trancheId];
+	tranche_name = GetLWTrancheNameByID(trancheId);
+
+	Assert(tranche_name);
+
+	return tranche_name;
 }
 
 /*
@@ -1995,3 +2107,121 @@ LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
 	}
 	return false;
 }
+
+static Size
+LWLockTrancheNamesInitSize()
+{
+	Size		sz;
+
+	sz = 256 * 1024;
+
+	Assert(dsa_minimum_size() <= sz);
+
+	return MAXALIGN(sz);
+}
+
+Size
+LWLockTrancheNamesShmemSize(void)
+{
+	Size		sz;
+
+	sz = MAXALIGN(sizeof(LWLockTrancheNamesStruct));
+	sz = add_size(sz, LWLockTrancheNamesInitSize());
+
+	return sz;
+}
+
+void
+LWLockTrancheNamesShmemInit(void)
+{
+	bool		found;
+
+	/* Initialize or attach to shared memory structure */
+	LWLockTrancheNames.shmem = (LWLockTrancheNamesShmem *)
+		ShmemInitStruct("Dynamic LWLock tranche names",
+						LWLockTrancheNamesShmemSize(),
+						&found);
+
+	if (!IsUnderPostmaster)
+	{
+		dsa_area   *dsa;
+		LWLockTrancheNamesShmem *ctl = LWLockTrancheNames.shmem;
+		char	   *p = (char *) ctl;
+
+		/* Calculate layout within the shared memory region */
+		p += MAXALIGN(sizeof(LWLockTrancheNamesShmem));
+		ctl->raw_dsa_area = p;
+		p += MAXALIGN(LWLockTrancheNamesInitSize());
+
+		LWLockInitialize(&ctl->lock, LWTRANCHE_LWLOCK_TRANCHE_NAMES_LIST);
+
+		/* Create a dynamic shared memory area in-place */
+		dsa = dsa_create_in_place(ctl->raw_dsa_area,
+								  LWLockTrancheNamesInitSize(),
+								  LWTRANCHE_LWLOCK_TRANCHE_NAMES_DSA,
+								  NULL);
+
+		/*
+		 * Postmaster will never access these again, thus free the local
+		 * dsa/dshash references.
+		 */
+		dsa_pin(dsa);
+		dsa_detach(dsa);
+
+		ctl->allocated = 0;
+		ctl->count = 0;
+		ctl->list_ptr = InvalidDsaPointer;
+	}
+	else
+	{
+		Assert(found);
+	}
+}
+
+static void
+LWLockTrancheNamesAttach(void)
+{
+	MemoryContext oldcontext;
+
+	Assert(LWLockTrancheNames.dsa == NULL);
+
+	/* stats shared memory persists for the backend lifetime */
+	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+	LWLockTrancheNames.dsa = dsa_attach_in_place(LWLockTrancheNames.shmem->raw_dsa_area,
+												 NULL);
+	dsa_pin_mapping(LWLockTrancheNames.dsa);
+
+	Assert(LWLockTrancheNames.dsa);
+
+	MemoryContextSwitchTo(oldcontext);
+}
+
+static void
+LWLockTrancheNamesDetach(void)
+{
+	Assert(LWLockTrancheNames.dsa);
+
+	dsa_detach(LWLockTrancheNames.dsa);
+
+	dsa_release_in_place(LWLockTrancheNames.shmem->raw_dsa_area);
+
+	LWLockTrancheNames.dsa = NULL;
+}
+
+static void
+LWLockTrancheNamesShutdownHook(int code, Datum arg)
+{
+	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
+
+	LWLockTrancheNamesDetach();
+}
+
+void
+LWLockTrancheNamesBEInit(void)
+{
+	LWLockTrancheNamesAttach();
+
+	/* Set up a process-exit hook to clean up */
+	before_shmem_exit(LWLockTrancheNamesShutdownHook, 0);
+}
diff --git a/src/backend/utils/activity/wait_event_names.txt b/src/backend/utils/activity/wait_event_names.txt
index 0be307d2ca0..ac827f96ad6 100644
--- a/src/backend/utils/activity/wait_event_names.txt
+++ b/src/backend/utils/activity/wait_event_names.txt
@@ -406,6 +406,8 @@ 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."
+LWLockDynamicTranchesDSA	"Waiting to access LWLock tranche named dynamic shared memory."
+LWLockDynamicTranchesList	"Waiting to access the list of LWLock tranche names in shared memory."
 
 # No "ABI_compatibility" region here as WaitEventLWLock has its own C code.
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 641e535a73c..1ef04c6b5ec 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -662,6 +662,8 @@ BaseInit(void)
 	 * drop ephemeral slots, which in turn triggers stats reporting.
 	 */
 	ReplicationSlotInitialize();
+
+	LWLockTrancheNamesBEInit();
 }
 
 
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 5e717765764..8a008fc67dd 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -156,22 +156,18 @@ extern void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
 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.
+ * An alternative, more flexible method of obtaining lwlocks is available.
+ * First, call LWLockNewTrancheId once, providing an associated tranche name.
+ * This function allocates a tranche ID from a shared counter. Then, for each
+ * lwlock, call LWLockInitialize once, 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);
 
+extern void LWLockTrancheNamesShmemInit(void);
+extern Size LWLockTrancheNamesShmemSize(void);
+extern void LWLockTrancheNamesBEInit(void);
+
 /*
  * Every tranche ID less than NUM_INDIVIDUAL_LWLOCKS is reserved; also,
  * we reserve additional tranche IDs for builtin tranches not included in
diff --git a/src/include/storage/lwlocklist.h b/src/include/storage/lwlocklist.h
index 208d2e3a8ed..5978648c09d 100644
--- a/src/include/storage/lwlocklist.h
+++ b/src/include/storage/lwlocklist.h
@@ -135,3 +135,5 @@ PG_LWLOCKTRANCHE(SUBTRANS_SLRU, SubtransSLRU)
 PG_LWLOCKTRANCHE(XACT_SLRU, XactSLRU)
 PG_LWLOCKTRANCHE(PARALLEL_VACUUM_DSA, ParallelVacuumDSA)
 PG_LWLOCKTRANCHE(AIO_URING_COMPLETION, AioUringCompletion)
+PG_LWLOCKTRANCHE(LWLOCK_TRANCHE_NAMES_DSA, LWLockDynamicTranchesDSA)
+PG_LWLOCKTRANCHE(LWLOCK_TRANCHE_NAMES_LIST, LWLockDynamicTranchesList)
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 32de6a3123e..4ae826d08a0 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)

