From 04727b3f141de4923df6beebd2664321f132269f Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-172-31-38-230.ec2.internal>
Date: Tue, 12 Aug 2025 19:20:47 +0000
Subject: [PATCH v7 2/2] lwlock shared tranche names test

---
 src/backend/storage/lmgr/lwlock.c             |  33 ++++-
 src/backend/utils/activity/wait_event.c       |   3 -
 src/include/storage/lwlock.h                  |  24 +++
 src/include/utils/wait_classes.h              |   2 +
 src/test/modules/Makefile                     |   3 +-
 src/test/modules/meson.build                  |   1 +
 .../modules/test_lwlock_tranches/Makefile     |  25 ++++
 .../modules/test_lwlock_tranches/meson.build  |  36 +++++
 .../t/001_test_lwlock_tranches.pl             | 117 +++++++++++++++
 .../test_lwlock_tranches--1.0.sql             |  16 ++
 .../test_lwlock_tranches.c                    | 138 ++++++++++++++++++
 .../test_lwlock_tranches.control              |   6 +
 12 files changed, 399 insertions(+), 5 deletions(-)
 create mode 100644 src/test/modules/test_lwlock_tranches/Makefile
 create mode 100644 src/test/modules/test_lwlock_tranches/meson.build
 create mode 100644 src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
 create mode 100644 src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control

diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 7bf455beda4..aa99df36feb 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -85,6 +85,7 @@
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
 #include "storage/spin.h"
+#include "utils/injection_point.h"
 #include "utils/memutils.h"
 
 #ifdef LWLOCK_STATS
@@ -652,6 +653,9 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name)
 	char	   *str_addr;
 	int			len;
 	int			current_allocated;
+#ifdef USE_INJECTION_POINTS
+	LWLockTranchesInjectionPoint condition = {SET_SHARED, false, NULL, 0, false, false};
+#endif
 
 	LWLockAcquire(&LWLockTrancheNames.shmem->lock, LW_EXCLUSIVE);
 
@@ -668,6 +672,10 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name)
 
 		name_ptrs = dsa_get_address(LWLockTrancheNames.dsa, LWLockTrancheNames.shmem->list_ptr);
 		memset(name_ptrs, InvalidDsaPointer, init_alloc);
+#ifdef USE_INJECTION_POINTS
+		condition.initialized = true;
+		condition.allocated = init_alloc;
+#endif
 	}
 
 	/*
@@ -695,6 +703,10 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name)
 
 		LWLockTrancheNames.shmem->list_ptr = new_list;
 		LWLockTrancheNames.shmem->allocated = new_alloc;
+#ifdef USE_INJECTION_POINTS
+		condition.resized = true;
+		condition.allocated = new_alloc;
+#endif
 	}
 	/* Use the current list */
 	else
@@ -712,6 +724,10 @@ SetSharedTrancheName(int tranche_index, const char *tranche_name)
 	name_ptrs[tranche_index] = str_ptr;
 
 	LWLockRelease(&LWLockTrancheNames.shmem->lock);
+#ifdef USE_INJECTION_POINTS
+	if (condition.resized || condition.initialized)
+		INJECTION_POINT("lwlock-sync-tranche-names", &condition);
+#endif
 }
 
 /*
@@ -900,6 +916,9 @@ static const char *
 GetLWTrancheName(uint16 trancheId)
 {
 	const char *tranche_name = NULL;
+#ifdef USE_INJECTION_POINTS
+	LWLockTranchesInjectionPoint condition = {SYNC_LOCAL, false, NULL};
+#endif
 
 	/* Built-in tranche or individual LWLock? */
 	if (trancheId < LWTRANCHE_FIRST_USER_DEFINED)
@@ -915,12 +934,24 @@ GetLWTrancheName(uint16 trancheId)
 	 */
 	if (trancheId >= LWLockTrancheNames.allocated ||
 		LWLockTrancheNames.local[trancheId] == NULL)
+	{
+#ifdef USE_INJECTION_POINTS
+		condition.synced_local = true;
+#endif
 		SyncLWLockTrancheNames(trancheId);
+	}
 
 	if (trancheId < LWLockTrancheNames.allocated)
 		tranche_name = LWLockTrancheNames.local[trancheId];
 
-	return tranche_name ? tranche_name : "extension";
+	tranche_name = tranche_name ? tranche_name : "extension";
+#ifdef USE_INJECTION_POINTS
+	condition.tranche_name = tranche_name;
+	condition.tranche_index = trancheId;
+	condition.tranche_id = trancheId + LWTRANCHE_FIRST_USER_DEFINED;
+	INJECTION_POINT("lwlock-sync-tranche-names", &condition);
+#endif
+	return tranche_name;
 }
 
 /*
diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c
index d9b8f34a355..eba7d338c1f 100644
--- a/src/backend/utils/activity/wait_event.c
+++ b/src/backend/utils/activity/wait_event.c
@@ -39,9 +39,6 @@ static const char *pgstat_get_wait_io(WaitEventIO w);
 static uint32 local_my_wait_event_info;
 uint32	   *my_wait_event_info = &local_my_wait_event_info;
 
-#define WAIT_EVENT_CLASS_MASK	0xFF000000
-#define WAIT_EVENT_ID_MASK		0x0000FFFF
-
 /*
  * Hash tables for storing custom wait event ids and their names in
  * shared memory.
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 8a008fc67dd..8cf0fa7256a 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -80,6 +80,30 @@ typedef struct NamedLWLockTranche
 	char	   *trancheName;
 } NamedLWLockTranche;
 
+#ifdef USE_INJECTION_POINTS
+typedef enum LWLockTranchesInjectionPointTests
+{
+	SYNC_LOCAL = 0,
+	SET_SHARED,
+}			LWLockTranchesInjectionPointTests;
+
+typedef struct LWLockTranchesInjectionPoint
+{
+	int			test_type;
+
+	/* for SYNC_LOCAL tests */
+	bool		synced_local;
+	const char *tranche_name;
+	int			tranche_index;
+	int			tranche_id;
+
+	/* for SET_SHARED tests */
+	int			allocated;
+	bool		initialized;
+	bool		resized;
+}			LWLockTranchesInjectionPoint;
+#endif
+
 extern PGDLLIMPORT NamedLWLockTranche *NamedLWLockTrancheArray;
 extern PGDLLIMPORT int NamedLWLockTrancheRequests;
 
diff --git a/src/include/utils/wait_classes.h b/src/include/utils/wait_classes.h
index 51ee68397d5..6ca0504cee1 100644
--- a/src/include/utils/wait_classes.h
+++ b/src/include/utils/wait_classes.h
@@ -10,6 +10,8 @@
 #ifndef WAIT_CLASSES_H
 #define WAIT_CLASSES_H
 
+#define WAIT_EVENT_CLASS_MASK	0xFF000000
+#define WAIT_EVENT_ID_MASK		0x0000FFFF
 
 /* ----------
  * Wait Classes
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 903a8ac151a..119d4708357 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -44,7 +44,8 @@ SUBDIRS = \
 		  test_tidstore \
 		  unsafe_tests \
 		  worker_spi \
-		  xid_wraparound
+		  xid_wraparound \
+		  test_lwlock_tranches \
 
 
 ifeq ($(enable_injection_points),yes)
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 93be0f57289..f04910d13d7 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -45,3 +45,4 @@ subdir('typcache')
 subdir('unsafe_tests')
 subdir('worker_spi')
 subdir('xid_wraparound')
+subdir('test_lwlock_tranches')
diff --git a/src/test/modules/test_lwlock_tranches/Makefile b/src/test/modules/test_lwlock_tranches/Makefile
new file mode 100644
index 00000000000..902867229c1
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/Makefile
@@ -0,0 +1,25 @@
+# src/test/modules/test_lwlock_tranches/Makefile
+
+MODULE_big = test_lwlock_tranches
+OBJS = \
+	$(WIN32RES) \
+	test_lwlock_tranches.o
+PGFILEDESC = "test_lwlock_tranches - test code LWLock tranche management"
+
+EXTENSION = test_lwlock_tranches
+DATA = test_lwlock_tranches--1.0.sql
+
+TAP_TESTS = 1
+
+export enable_injection_points
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_lwlock_tranches
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_lwlock_tranches/meson.build b/src/test/modules/test_lwlock_tranches/meson.build
new file mode 100644
index 00000000000..f32417a4143
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/meson.build
@@ -0,0 +1,36 @@
+# Copyright (c) 2024-2025, PostgreSQL Global Development Group
+
+test_lwlock_tranches_sources = files(
+  'test_lwlock_tranches.c',
+)
+
+if host_system == 'windows'
+  test_lwlock_tranches_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_lwlock_tranches',
+    '--FILEDESC', 'test_lwlock_tranches - test code LWLock tranche management',])
+endif
+
+test_lwlock_tranches = shared_module('test_lwlock_tranches',
+  test_lwlock_tranches_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_lwlock_tranches
+
+test_install_data += files(
+  'test_lwlock_tranches.control',
+  'test_lwlock_tranches--1.0.sql',
+)
+
+tests += {
+  'name': 'test_lwlock_tranches',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'env': {
+       'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+    },
+    'tests': [
+      't/001_test_lwlock_tranches.pl',
+    ],
+  },
+}
\ No newline at end of file
diff --git a/src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl b/src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl
new file mode 100644
index 00000000000..843485bf939
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/t/001_test_lwlock_tranches.pl
@@ -0,0 +1,117 @@
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $initial_size = 16;
+my $final_allocation = 64;
+
+SKIP:
+{
+	skip 'Injection points not supported by this build', 1
+		unless $ENV{enable_injection_points} eq 'yes';
+
+	my $node = PostgreSQL::Test::Cluster->new('primary');
+	$node->init();
+	$node->append_conf('postgresql.conf',
+		qq(shared_preload_libraries=test_lwlock_tranches));
+	$node->append_conf('postgresql.conf',
+		qq(test_lwlock_tranches.requested_named_tranches=2));
+	$node->start();
+
+	$node->safe_psql('postgres', q(CREATE EXTENSION injection_points));
+	$node->safe_psql('postgres', q(CREATE EXTENSION test_lwlock_tranches));
+
+	my $first_user_defined = $node->safe_psql(
+		'postgres',
+		"SELECT test_lwlock_tranches_get_first_user_defined()");
+
+	my $requested_named_tranches = $node->safe_psql(
+		'postgres',
+		"SELECT setting FROM pg_settings WHERE name = \'test_lwlock_tranches.requested_named_tranches\'");
+
+	my $next_index;
+	my $lookup_tranche_id = 0;
+	my $log_location = -s $node->logfile;
+	my $current_size = $initial_size;
+
+	while ($current_size < $final_allocation) {
+		$current_size *= 2;
+	}
+
+	#
+	# Create tranches using LWLockNewTrancheId. Note that tranches created
+	# with RequestNamedLWLockTranche occur during startup, based on
+	# test_lwlock_tranches.requested_named_tranches.
+	#
+	$node->safe_psql('postgres',
+		qq{select test_lwlock_new_tranche_id($current_size - $requested_named_tranches)});
+
+	$log_location = $node->wait_for_log(
+		qr/ LOG:  allocation_reason = resize allocation_size = $current_size/, $log_location);
+	ok(1, "resize shared memory with allocation up to $current_size tranche names");
+
+	# Tests: Lookup of tranches created with RequestNamedLWLockTranche
+	for ($next_index = 0; $next_index < $requested_named_tranches; $next_index++) {
+		$lookup_tranche_id = $next_index + $first_user_defined;
+
+		$node->safe_psql('postgres',
+			qq{select test_get_lwlock_identifier($next_index)});
+		$log_location = $node->wait_for_log(
+			qr/ LOG:  tranche_name = test_lock_$next_index tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = no/, $log_location);
+	}
+	ok(1, "requested_named_tranches looked up from local cache");
+
+	#
+	# Lookup for tranches created directly with LWLockNewTrancheId.
+	#
+	# Two scenarios are tested. First, the lookup for tranches created directly with
+	# LWLockNewTrancheId is performed twice to ensure the second lookup occurs from the
+	# local cache. Second, the lookup verifies that tranches created during postmaster
+	# startup, which are already in local memory, are not overwritten by synchronization
+	# of local memory.
+	#
+	$lookup_tranche_id = $next_index + $first_user_defined;
+	$node->safe_psql('postgres',
+		qq{select  test_get_lwlock_identifier($next_index);
+		select test_get_lwlock_identifier($next_index);
+		select test_get_lwlock_identifier($next_index);
+		select test_get_lwlock_identifier(0);
+		select test_get_lwlock_identifier(1)});
+	$node->wait_for_log(qr/ LOG:  tranche_name = test_lock__$next_index tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = yes/, $log_location);
+	$node->wait_for_log(qr/ LOG:  tranche_name = test_lock__$next_index tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = no/, $log_location);
+	$node->wait_for_log(qr/ LOG:  tranche_name = test_lock__$next_index tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = no/, $log_location);
+	$lookup_tranche_id = $first_user_defined;
+	$node->wait_for_log(qr/ LOG:  tranche_name = test_lock_0 tranche_id = $lookup_tranche_id tranche_index = 0 synced_local = no/, $log_location);
+	$lookup_tranche_id = $first_user_defined + 1;
+	$log_location = $node->wait_for_log(qr/ LOG:  tranche_name = test_lock_1 tranche_id = $lookup_tranche_id tranche_index = 1 synced_local = no/, $log_location);
+	ok(1, "lookup with synchronization is successful");
+
+	#
+	# Lookup tranches that have not been registered with RequestNamedLWLockTranche
+	# or LWLockNewTrancheId. They should return "extension". We want to test when the
+	# tranche index is within the allocated slots in shared memory and outisde of the
+	# allocated slots.
+	#
+	$next_index = $current_size - $requested_named_tranches;
+	$lookup_tranche_id = $next_index + $first_user_defined;
+	$node->safe_psql('postgres',
+		qq{select test_get_lwlock_identifier($next_index);
+		select test_get_lwlock_identifier($next_index);});
+	$node->wait_for_log(qr/ LOG:  tranche_name = extension tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = yes/, $log_location);
+	$log_location = $node->wait_for_log(qr/ LOG:  tranche_name = extension tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = no/, $log_location);
+	ok(1, 'unnamed tranche_id is within allocated shared memory and returns "extension"');
+
+	$next_index = $current_size;
+	$lookup_tranche_id = $next_index + $first_user_defined;
+	$node->safe_psql('postgres',
+		qq{select test_get_lwlock_identifier($next_index);
+		select test_get_lwlock_identifier($next_index)});
+	$node->wait_for_log(qr/LOG:  tranche_name = extension tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = no/, $log_location);
+	$log_location = $node->wait_for_log(qr/LOG:  tranche_name = extension tranche_id = $lookup_tranche_id tranche_index = $next_index synced_local = yes/, $log_location);
+	ok(1, 'unnamed tranche_id is outside of allocated shared memory and returns "extension"');
+}
+
+done_testing();
\ No newline at end of file
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
new file mode 100644
index 00000000000..7fdf90e3e23
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql
@@ -0,0 +1,16 @@
+-- test_lwlock_tranches--1.0.sql
+
+CREATE FUNCTION test_lwlock_new_tranche_id(bigint)
+RETURNS void
+AS 'MODULE_PATHNAME', 'test_lwlock_new_tranche_id'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION test_get_lwlock_identifier(int)
+RETURNS void
+AS 'MODULE_PATHNAME', 'test_get_lwlock_identifier'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION test_lwlock_tranches_get_first_user_defined()
+RETURNS int
+AS 'MODULE_PATHNAME', 'test_lwlock_tranches_get_first_user_defined'
+LANGUAGE C STRICT;
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
new file mode 100644
index 00000000000..4433984b833
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
@@ -0,0 +1,138 @@
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/dsm_registry.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "utils/guc.h"
+#include "utils/injection_point.h"
+#include "utils/wait_classes.h"
+
+PG_MODULE_MAGIC;
+
+extern PGDLLEXPORT void lwlock_tranches_injection_callback(const char *name,
+														   const void *private_data,
+														   void *arg);
+
+/* hooks */
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
+static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
+static void test_lwlock_tranches_shmem_request(void);
+static void test_lwlock_tranches_shmem_startup(void);
+
+/* GUC */
+static int	test_lwlock_tranches_requested_named_tranches = 0;
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+	prev_shmem_request_hook = shmem_request_hook;
+	shmem_request_hook = test_lwlock_tranches_shmem_request;
+	prev_shmem_startup_hook = shmem_startup_hook;
+	shmem_startup_hook = test_lwlock_tranches_shmem_startup;
+
+	DefineCustomIntVariable("test_lwlock_tranches.requested_named_tranches",
+							"Sets the number of locks created during shmem request",
+							NULL,
+							&test_lwlock_tranches_requested_named_tranches,
+							2,
+							1,
+							UINT16_MAX,
+							PGC_POSTMASTER,
+							0,
+							NULL,
+							NULL,
+							NULL);
+}
+
+static void
+test_lwlock_tranches_shmem_startup(void)
+{
+	if (prev_shmem_startup_hook)
+		prev_shmem_startup_hook();
+
+#ifdef USE_INJECTION_POINTS
+	InjectionPointAttach("lwlock-sync-tranche-names",
+						 "test_lwlock_tranches",
+						 "lwlock_tranches_injection_callback",
+						 NULL,
+						 0);
+#else
+	elog(ERROR, "injection points not supported");
+#endif
+}
+
+static void
+test_lwlock_tranches_shmem_request(void)
+{
+	if (prev_shmem_request_hook)
+		prev_shmem_request_hook();
+
+	for (int i = 0; i < test_lwlock_tranches_requested_named_tranches; i++)
+	{
+		char		name[15];
+
+		snprintf(name, sizeof(name), "test_lock_%d", i);
+		RequestNamedLWLockTranche(name, i);
+	}
+}
+
+PG_FUNCTION_INFO_V1(test_lwlock_new_tranche_id);
+Datum
+test_lwlock_new_tranche_id(PG_FUNCTION_ARGS)
+{
+	int64		num = PG_GETARG_INT64(0);
+
+	for (int i = test_lwlock_tranches_requested_named_tranches; i < num; i++)
+	{
+		char		name[50];
+
+		snprintf(name, 50, "test_lock__%d", i);
+
+		LWLockNewTrancheId(name);
+	}
+
+	PG_RETURN_VOID();
+}
+
+void
+lwlock_tranches_injection_callback(const char *name, const void *private_data, void *arg)
+{
+#ifdef USE_INJECTION_POINTS
+	LWLockTranchesInjectionPoint *condition = (LWLockTranchesInjectionPoint *) arg;
+
+	if (condition->test_type == SYNC_LOCAL)
+		elog(LOG, "tranche_name = %s tranche_id = %d tranche_index = %d synced_local = %s",
+			 condition->tranche_name,
+			 condition->tranche_id,
+			 condition->tranche_index,
+			 condition->synced_local ? "yes" : "no");
+	else if (condition->test_type == SET_SHARED)
+		elog(LOG, "allocation_reason = %s allocation_size = %d",
+			 condition->initialized ? "initial" : "resize",
+			 condition->allocated);
+#endif
+}
+
+PG_FUNCTION_INFO_V1(test_get_lwlock_identifier);
+Datum
+test_get_lwlock_identifier(PG_FUNCTION_ARGS)
+{
+	int			id = PG_GETARG_INT32(0);
+	uint16		eventid = id + LWTRANCHE_FIRST_USER_DEFINED;
+
+	GetLWLockIdentifier(PG_WAIT_LWLOCK & WAIT_EVENT_CLASS_MASK, eventid);
+
+	PG_RETURN_VOID();
+}
+
+PG_FUNCTION_INFO_V1(test_lwlock_tranches_get_first_user_defined);
+Datum
+test_lwlock_tranches_get_first_user_defined(PG_FUNCTION_ARGS)
+{
+	return LWTRANCHE_FIRST_USER_DEFINED;
+}
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control
new file mode 100644
index 00000000000..bf0ecf64376
--- /dev/null
+++ b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control
@@ -0,0 +1,6 @@
+# test_lwlock_tranches.control
+
+comment = 'Test LWLock tranch names tracking'
+default_version = '1.0'
+relocatable = false
+module_pathname = '$libdir/test_lwlock_tranches'
\ No newline at end of file
-- 
2.43.0

