Add a test harness for the LWLock tranche code.
authorNathan Bossart <nathan@postgresql.org>
Thu, 18 Sep 2025 20:23:11 +0000 (15:23 -0500)
committerNathan Bossart <nathan@postgresql.org>
Thu, 18 Sep 2025 20:23:11 +0000 (15:23 -0500)
This code is heavily used and already has decent test coverage, but
it lacks a dedicated test suite.  This commit changes that.

Author: Sami Imseih <samimseih@gmail.com>
Co-authored-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Discussion: https://postgr.es/m/CAA5RZ0tQ%2BEYSTOd2hQ8RXdsNfGBLAtOe-YmnsTE6ZVg0E-4qew%40mail.gmail.com
Discussion: https://postgr.es/m/CAA5RZ0vpr0P2rbA%3D_K0_SCHM7bmfVX4wEO9FAyopN1eWCYORhA%40mail.gmail.com

src/test/modules/Makefile
src/test/modules/meson.build
src/test/modules/test_lwlock_tranches/.gitignore [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/Makefile [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/meson.build [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/sql/test_lwlock_tranches.sql [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf [new file with mode: 0644]
src/test/modules/test_lwlock_tranches/test_lwlock_tranches.control [new file with mode: 0644]

index 903a8ac151aa1ff7eb5ce3ebd15141edf312acee..8a3cd2afab7f284a20ea1f9ed0431264597fff82 100644 (file)
@@ -29,6 +29,7 @@ SUBDIRS = \
          test_integerset \
          test_json_parser \
          test_lfind \
+         test_lwlock_tranches \
          test_misc \
          test_oat_hooks \
          test_parser \
index 93be0f57289a817821c7504d72762e47c5f48041..717e85066ba736316dd1d6cd287280cd37b9c1ef 100644 (file)
@@ -28,6 +28,7 @@ subdir('test_int128')
 subdir('test_integerset')
 subdir('test_json_parser')
 subdir('test_lfind')
+subdir('test_lwlock_tranches')
 subdir('test_misc')
 subdir('test_oat_hooks')
 subdir('test_parser')
diff --git a/src/test/modules/test_lwlock_tranches/.gitignore b/src/test/modules/test_lwlock_tranches/.gitignore
new file mode 100644 (file)
index 0000000..5dcb3ff
--- /dev/null
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_lwlock_tranches/Makefile b/src/test/modules/test_lwlock_tranches/Makefile
new file mode 100644 (file)
index 0000000..e357b7d
--- /dev/null
@@ -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 for LWLock tranches allocated by extensions"
+
+EXTENSION = test_lwlock_tranches
+DATA = test_lwlock_tranches--1.0.sql
+
+REGRESS_OPTS = --temp-config $(top_srcdir)/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf
+REGRESS = test_lwlock_tranches
+NO_INSTALLCHECK = 1
+
+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/expected/test_lwlock_tranches.out b/src/test/modules/test_lwlock_tranches/expected/test_lwlock_tranches.out
new file mode 100644 (file)
index 0000000..688a20b
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE EXTENSION test_lwlock_tranches;
+SELECT test_lwlock_tranches();
+ test_lwlock_tranches 
+----------------------
+(1 row)
+
+SELECT test_lwlock_tranche_creation(NULL);
+ERROR:  tranche name cannot be NULL
+SELECT test_lwlock_tranche_creation(repeat('a', 64));
+ERROR:  tranche name too long
+DETAIL:  LWLock tranche names must be no longer than 63 bytes.
+SELECT test_lwlock_tranche_creation('test');
+ERROR:  maximum number of tranches already registered
+DETAIL:  No more than 256 tranches may be registered.
+SELECT test_lwlock_tranche_lookup('test_lwlock_tranches_startup');
+ test_lwlock_tranche_lookup 
+----------------------------
+(1 row)
+
+SELECT test_lwlock_tranche_lookup('bogus');
+ERROR:  requested tranche is not registered
+SELECT test_lwlock_initialize(65535);
+ERROR:  tranche 65535 is not registered
diff --git a/src/test/modules/test_lwlock_tranches/meson.build b/src/test/modules/test_lwlock_tranches/meson.build
new file mode 100644 (file)
index 0000000..755c2b7
--- /dev/null
@@ -0,0 +1,35 @@
+# Copyright (c) 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 for LWLock tranches allocated by extensions',])
+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(),
+  'regress': {
+    'sql': [
+      'test_lwlock_tranches',
+    ],
+    'regress_args': ['--temp-config', files('test_lwlock_tranches.conf')],
+    'runningcheck': false,
+  },
+}
diff --git a/src/test/modules/test_lwlock_tranches/sql/test_lwlock_tranches.sql b/src/test/modules/test_lwlock_tranches/sql/test_lwlock_tranches.sql
new file mode 100644 (file)
index 0000000..4d085b8
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE EXTENSION test_lwlock_tranches;
+SELECT test_lwlock_tranches();
+SELECT test_lwlock_tranche_creation(NULL);
+SELECT test_lwlock_tranche_creation(repeat('a', 64));
+SELECT test_lwlock_tranche_creation('test');
+SELECT test_lwlock_tranche_lookup('test_lwlock_tranches_startup');
+SELECT test_lwlock_tranche_lookup('bogus');
+SELECT test_lwlock_initialize(65535);
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 (file)
index 0000000..3482198
--- /dev/null
@@ -0,0 +1,16 @@
+/* src/test/modules/test_lwlock_tranches/test_lwlock_tranches--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_lwlock_tranches" to load this file. \quit
+
+CREATE FUNCTION test_lwlock_tranches() RETURNS VOID
+   AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_lwlock_tranche_creation(tranche_name TEXT) RETURNS VOID
+   AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_lwlock_tranche_lookup(tranche_name TEXT) RETURNS VOID
+   AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_lwlock_initialize(tranche_id INT) RETURNS VOID
+   AS 'MODULE_PATHNAME' LANGUAGE C;
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 (file)
index 0000000..a4f7fb2
--- /dev/null
@@ -0,0 +1,123 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_lwlock_tranches.c
+ *     Test code for LWLock tranches allocated by extensions.
+ *
+ * Copyright (c) 2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *     src/test/modules/test_lwlock_tranches/test_lwlock_tranches.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/lwlock.h"
+#include "utils/builtins.h"
+#include "utils/wait_classes.h"
+
+PG_MODULE_MAGIC;
+
+#define STARTUP_TRANCHE_NAME "test_lwlock_tranches_startup"
+#define DYNAMIC_TRANCHE_NAME "test_lwlock_tranches_dynamic"
+
+#define NUM_STARTUP_TRANCHES (32)
+#define NUM_DYNAMIC_TRANCHES (256 - NUM_STARTUP_TRANCHES)
+
+#define GET_TRANCHE_NAME(a) GetLWLockIdentifier(PG_WAIT_LWLOCK, (a))
+
+static shmem_request_hook_type prev_shmem_request_hook;
+static void test_lwlock_tranches_shmem_request(void);
+
+void
+_PG_init(void)
+{
+   prev_shmem_request_hook = shmem_request_hook;
+   shmem_request_hook = test_lwlock_tranches_shmem_request;
+}
+
+static void
+test_lwlock_tranches_shmem_request(void)
+{
+   if (prev_shmem_request_hook)
+       prev_shmem_request_hook();
+
+   for (int i = 0; i < NUM_STARTUP_TRANCHES; i++)
+       RequestNamedLWLockTranche(STARTUP_TRANCHE_NAME, 1);
+}
+
+/*
+ * Checks that GetLWLockIdentifier() returns the expected value for tranches
+ * registered via RequestNamedLWLockTranche() and LWLockNewTrancheId().
+ */
+PG_FUNCTION_INFO_V1(test_lwlock_tranches);
+Datum
+test_lwlock_tranches(PG_FUNCTION_ARGS)
+{
+   int         dynamic_tranches[NUM_DYNAMIC_TRANCHES];
+
+   for (int i = 0; i < NUM_DYNAMIC_TRANCHES; i++)
+       dynamic_tranches[i] = LWLockNewTrancheId(DYNAMIC_TRANCHE_NAME);
+
+   for (int i = 0; i < NUM_STARTUP_TRANCHES; i++)
+   {
+       if (strcmp(GET_TRANCHE_NAME(LWTRANCHE_FIRST_USER_DEFINED + i),
+                  STARTUP_TRANCHE_NAME) != 0)
+           elog(ERROR, "incorrect startup lock tranche name");
+   }
+
+   for (int i = 0; i < NUM_DYNAMIC_TRANCHES; i++)
+   {
+       if (strcmp(GET_TRANCHE_NAME(dynamic_tranches[i]),
+                  DYNAMIC_TRANCHE_NAME) != 0)
+           elog(ERROR, "incorrect dynamic lock tranche name");
+   }
+
+   PG_RETURN_VOID();
+}
+
+/*
+ * Wrapper for LWLockNewTrancheId().
+ */
+PG_FUNCTION_INFO_V1(test_lwlock_tranche_creation);
+Datum
+test_lwlock_tranche_creation(PG_FUNCTION_ARGS)
+{
+   char       *tranche_name = PG_ARGISNULL(0) ? NULL : TextDatumGetCString(PG_GETARG_DATUM(0));
+
+   (void) LWLockNewTrancheId(tranche_name);
+
+   PG_RETURN_VOID();
+}
+
+/*
+ * Wrapper for GetNamedLWLockTranche().
+ */
+PG_FUNCTION_INFO_V1(test_lwlock_tranche_lookup);
+Datum
+test_lwlock_tranche_lookup(PG_FUNCTION_ARGS)
+{
+   char       *tranche_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+   (void) GetNamedLWLockTranche(tranche_name);
+
+   PG_RETURN_VOID();
+}
+
+/*
+ * Wrapper for LWLockInitialize().
+ */
+PG_FUNCTION_INFO_V1(test_lwlock_initialize);
+Datum
+test_lwlock_initialize(PG_FUNCTION_ARGS)
+{
+   int         tranche_id = PG_GETARG_INT32(0);
+   LWLock      lock;
+
+   LWLockInitialize(&lock, tranche_id);
+
+   PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf b/src/test/modules/test_lwlock_tranches/test_lwlock_tranches.conf
new file mode 100644 (file)
index 0000000..acbe5bf
--- /dev/null
@@ -0,0 +1 @@
+shared_preload_libraries = 'test_lwlock_tranches'
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 (file)
index 0000000..1cde04b
--- /dev/null
@@ -0,0 +1,4 @@
+comment = 'Test code for LWLock tranches allocated by extensions'
+default_version = '1.0'
+module_pathname = '$libdir/test_lwlock_tranches'
+relocatable = true