Cross-check lists of built-in LWLock tranches.
authorNathan Bossart <nathan@postgresql.org>
Wed, 23 Jul 2025 17:06:20 +0000 (12:06 -0500)
committerNathan Bossart <nathan@postgresql.org>
Wed, 23 Jul 2025 17:06:20 +0000 (12:06 -0500)
lwlock.c, lwlock.h, and wait_event_names.txt each contain a list of
built-in LWLock tranches.  It is easy to miss one or the other when
adding or removing tranches, and discrepancies have adverse effects
(e.g., breaking JOINs between pg_stat_activity and pg_wait_events).
This commit moves the lists of built-in tranches in lwlock.{c,h} to
lwlocklist.h and adds a cross-check to the script that generates
lwlocknames.h.  If the lists do not match exactly, building will
fail.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/aHpOgwuFQfcFMZ/B%40ip-10-97-1-34.eu-west-3.compute.internal

src/backend/storage/lmgr/generate-lwlocknames.pl
src/backend/storage/lmgr/lwlock.c
src/backend/utils/activity/wait_event_names.txt
src/include/storage/lwlock.h
src/include/storage/lwlocklist.h

index c7a6720440db6ab4b816c38c0d804e55df78d269..cd3e43c448aed010990c1f34ecc0572fe283805c 100644 (file)
@@ -27,18 +27,24 @@ print $h "/* there is deliberately not an #ifndef LWLOCKNAMES_H here */\n\n";
 
 
 #
-# First, record the predefined LWLocks listed in wait_event_names.txt.  We'll
-# cross-check those with the ones in lwlocklist.h.
+# First, record the predefined LWLocks and built-in tranches listed in
+# wait_event_names.txt.  We'll cross-check those with the ones in lwlocklist.h.
 #
+my @wait_event_tranches;
 my @wait_event_lwlocks;
 my $record_lwlocks = 0;
+my $in_tranches = 0;
 
 while (<$wait_event_names>)
 {
    chomp;
 
    # Check for end marker.
-   last if /^# END OF PREDEFINED LWLOCKS/;
+   if (/^# END OF PREDEFINED LWLOCKS/)
+   {
+       $in_tranches = 1;
+       next;
+   }
 
    # Skip comments and empty lines.
    next if /^#/;
@@ -54,13 +60,29 @@ while (<$wait_event_names>)
    # Go to the next line if we are not yet recording LWLocks.
    next if not $record_lwlocks;
 
+   # Stop recording if we reach another section.
+   last if /^Section:/;
+
    # Record the LWLock.
    (my $waiteventname, my $waitevendocsentence) = split(/\t/, $_);
-   push(@wait_event_lwlocks, $waiteventname);
+
+   if ($in_tranches)
+   {
+       push(@wait_event_tranches, $waiteventname);
+   }
+   else
+   {
+       push(@wait_event_lwlocks, $waiteventname);
+   }
 }
 
+#
+# While gathering the list of predefined LWLocks, cross-check the lists in
+# lwlocklist.h with the wait events we just recorded.
+#
 my $in_comment = 0;
-my $i = 0;
+my $lwlock_count = 0;
+my $tranche_count = 0;
 while (<$lwlocklist>)
 {
    chomp;
@@ -81,38 +103,72 @@ while (<$lwlocklist>)
        next;
    }
 
-   die "unable to parse lwlocklist.h line \"$_\""
-     unless /^PG_LWLOCK\((\d+),\s+(\w+)\)$/;
+   #
+   # Gather list of predefined LWLocks and cross-check with the wait events.
+   #
+   if (/^PG_LWLOCK\((\d+),\s+(\w+)\)$/)
+   {
+       my ($lockidx, $lockname) = ($1, $2);
 
-   (my $lockidx, my $lockname) = ($1, $2);
+       die "lwlocklist.h not in order" if $lockidx < $lastlockidx;
+       die "lwlocklist.h has duplicates" if $lockidx == $lastlockidx;
 
-   die "lwlocklist.h not in order" if $lockidx < $lastlockidx;
-   die "lwlocklist.h has duplicates" if $lockidx == $lastlockidx;
+       die "$lockname defined in lwlocklist.h but missing from "
+         . "wait_event_names.txt"
+         if $lwlock_count >= scalar @wait_event_lwlocks;
+       die "lists of predefined LWLocks do not match (first mismatch at "
+         . "$wait_event_lwlocks[$lwlock_count] in wait_event_names.txt and "
+         . "$lockname in lwlocklist.h)"
+         if $wait_event_lwlocks[$lwlock_count] ne $lockname;
 
-   die "$lockname defined in lwlocklist.h but missing from "
-     . "wait_event_names.txt"
-     if $i >= scalar @wait_event_lwlocks;
-   die "lists of predefined LWLocks do not match (first mismatch at "
-     . "$wait_event_lwlocks[$i] in wait_event_names.txt and $lockname in "
-     . "lwlocklist.h)"
-     if $wait_event_lwlocks[$i] ne $lockname;
-   $i++;
+       $lwlock_count++;
 
-   while ($lastlockidx < $lockidx - 1)
+       while ($lastlockidx < $lockidx - 1)
+       {
+           ++$lastlockidx;
+       }
+       $lastlockidx = $lockidx;
+
+       # Add a "Lock" suffix to each lock name, as the C code depends on that.
+       printf $h "#define %-32s (&MainLWLockArray[$lockidx].lock)\n",
+         $lockname . "Lock";
+
+       next;
+   }
+
+   #
+   # Cross-check the built-in LWLock tranches with the wait events.
+   #
+   if (/^PG_LWLOCKTRANCHE\((\w+),\s+(\w+)\)$/)
    {
-       ++$lastlockidx;
+       my ($tranche_id, $tranche_name) = ($1, $2);
+
+       die "$tranche_name defined in lwlocklist.h but missing from "
+         . "wait_event_names.txt"
+         if $tranche_count >= scalar @wait_event_tranches;
+       die
+         "lists of built-in LWLock tranches do not match (first mismatch at "
+         . "$wait_event_tranches[$tranche_count] in wait_event_names.txt and "
+         . "$tranche_name in lwlocklist.h)"
+         if $wait_event_tranches[$tranche_count] ne $tranche_name;
+
+       $tranche_count++;
+
+       next;
    }
-   $lastlockidx = $lockidx;
 
-   # Add a "Lock" suffix to each lock name, as the C code depends on that
-   printf $h "#define %-32s (&MainLWLockArray[$lockidx].lock)\n",
-     $lockname . "Lock";
+   die "unable to parse lwlocklist.h line \"$_\"";
 }
 
 die
-  "$wait_event_lwlocks[$i] defined in wait_event_names.txt but missing from "
-  . "lwlocklist.h"
-  if $i < scalar @wait_event_lwlocks;
+  "$wait_event_lwlocks[$lwlock_count] defined in wait_event_names.txt but "
+  . " missing from lwlocklist.h"
+  if $lwlock_count < scalar @wait_event_lwlocks;
+
+die
+  "$wait_event_tranches[$tranche_count] defined in wait_event_names.txt but "
+  . "missing from lwlocklist.h"
+  if $tranche_count < scalar @wait_event_tranches;
 
 print $h "\n";
 printf $h "#define NUM_INDIVIDUAL_LWLOCKS      %s\n", $lastlockidx + 1;
index 2d43bf2cc1323c8ae14fddbb861bbf6ef4d59a3d..ec9c345ffdfb88c825fb4a7c63dbd165d6af0e47 100644 (file)
@@ -122,9 +122,8 @@ StaticAssertDecl((LW_VAL_EXCLUSIVE & LW_FLAG_MASK) == 0,
  * own tranche.  We absorb the names of these tranches from there into
  * BuiltinTrancheNames here.
  *
- * 2. There are some predefined tranches for built-in groups of locks.
- * These are listed in enum BuiltinTrancheIds in lwlock.h, and their names
- * appear in BuiltinTrancheNames[] below.
+ * 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
@@ -135,49 +134,10 @@ StaticAssertDecl((LW_VAL_EXCLUSIVE & LW_FLAG_MASK) == 0,
  */
 static const char *const BuiltinTrancheNames[] = {
 #define PG_LWLOCK(id, lockname) [id] = CppAsString(lockname),
+#define PG_LWLOCKTRANCHE(id, lockname) [LWTRANCHE_##id] = CppAsString(lockname),
 #include "storage/lwlocklist.h"
 #undef PG_LWLOCK
-   [LWTRANCHE_XACT_BUFFER] = "XactBuffer",
-   [LWTRANCHE_COMMITTS_BUFFER] = "CommitTsBuffer",
-   [LWTRANCHE_SUBTRANS_BUFFER] = "SubtransBuffer",
-   [LWTRANCHE_MULTIXACTOFFSET_BUFFER] = "MultiXactOffsetBuffer",
-   [LWTRANCHE_MULTIXACTMEMBER_BUFFER] = "MultiXactMemberBuffer",
-   [LWTRANCHE_NOTIFY_BUFFER] = "NotifyBuffer",
-   [LWTRANCHE_SERIAL_BUFFER] = "SerialBuffer",
-   [LWTRANCHE_WAL_INSERT] = "WALInsert",
-   [LWTRANCHE_BUFFER_CONTENT] = "BufferContent",
-   [LWTRANCHE_REPLICATION_ORIGIN_STATE] = "ReplicationOriginState",
-   [LWTRANCHE_REPLICATION_SLOT_IO] = "ReplicationSlotIO",
-   [LWTRANCHE_LOCK_FASTPATH] = "LockFastPath",
-   [LWTRANCHE_BUFFER_MAPPING] = "BufferMapping",
-   [LWTRANCHE_LOCK_MANAGER] = "LockManager",
-   [LWTRANCHE_PREDICATE_LOCK_MANAGER] = "PredicateLockManager",
-   [LWTRANCHE_PARALLEL_HASH_JOIN] = "ParallelHashJoin",
-   [LWTRANCHE_PARALLEL_BTREE_SCAN] = "ParallelBtreeScan",
-   [LWTRANCHE_PARALLEL_QUERY_DSA] = "ParallelQueryDSA",
-   [LWTRANCHE_PER_SESSION_DSA] = "PerSessionDSA",
-   [LWTRANCHE_PER_SESSION_RECORD_TYPE] = "PerSessionRecordType",
-   [LWTRANCHE_PER_SESSION_RECORD_TYPMOD] = "PerSessionRecordTypmod",
-   [LWTRANCHE_SHARED_TUPLESTORE] = "SharedTupleStore",
-   [LWTRANCHE_SHARED_TIDBITMAP] = "SharedTidBitmap",
-   [LWTRANCHE_PARALLEL_APPEND] = "ParallelAppend",
-   [LWTRANCHE_PER_XACT_PREDICATE_LIST] = "PerXactPredicateList",
-   [LWTRANCHE_PGSTATS_DSA] = "PgStatsDSA",
-   [LWTRANCHE_PGSTATS_HASH] = "PgStatsHash",
-   [LWTRANCHE_PGSTATS_DATA] = "PgStatsData",
-   [LWTRANCHE_LAUNCHER_DSA] = "LogicalRepLauncherDSA",
-   [LWTRANCHE_LAUNCHER_HASH] = "LogicalRepLauncherHash",
-   [LWTRANCHE_DSM_REGISTRY_DSA] = "DSMRegistryDSA",
-   [LWTRANCHE_DSM_REGISTRY_HASH] = "DSMRegistryHash",
-   [LWTRANCHE_COMMITTS_SLRU] = "CommitTsSLRU",
-   [LWTRANCHE_MULTIXACTOFFSET_SLRU] = "MultiXactOffsetSLRU",
-   [LWTRANCHE_MULTIXACTMEMBER_SLRU] = "MultiXactMemberSLRU",
-   [LWTRANCHE_NOTIFY_SLRU] = "NotifySLRU",
-   [LWTRANCHE_SERIAL_SLRU] = "SerialSLRU",
-   [LWTRANCHE_SUBTRANS_SLRU] = "SubtransSLRU",
-   [LWTRANCHE_XACT_SLRU] = "XactSLRU",
-   [LWTRANCHE_PARALLEL_VACUUM_DSA] = "ParallelVacuumDSA",
-   [LWTRANCHE_AIO_URING_COMPLETION] = "AioUringCompletion",
+#undef PG_LWLOCKTRANCHE
 };
 
 StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
index 4da68312b5f971bc901dd3788cc0bb4aefbc3353..0be307d2ca04b58c90576265175bb9bf4212b8d7 100644 (file)
@@ -356,9 +356,13 @@ AioWorkerSubmissionQueue   "Waiting to access AIO worker submission queue."
 #
 # END OF PREDEFINED LWLOCKS (DO NOT CHANGE THIS LINE)
 #
-# Predefined LWLocks (i.e., those declared in lwlocknames.h) must be listed
-# in the section above and must be listed in the same order as in
-# lwlocknames.h.  Other LWLocks must be listed in the section below.
+# Predefined LWLocks (i.e., those declared at the top of lwlocknames.h) must be
+# listed in the section above and must be listed in the same order as in
+# lwlocknames.h.
+#
+# Likewise, the built-in LWLock tranches (i.e., those declared at the bottom of
+# lwlocknames.h) must be listed in the section below and must be listed in the
+# same order as in lwlocknames.h.
 #
 
 XactBuffer "Waiting for I/O on a transaction status SLRU buffer."
index 08a72569ae5fd5713af50c79d33266aa08b325aa..5e717765764f41c7cb92e61c34d17731dc41877c 100644 (file)
@@ -176,51 +176,23 @@ extern void LWLockInitialize(LWLock *lock, int tranche_id);
  * Every tranche ID less than NUM_INDIVIDUAL_LWLOCKS is reserved; also,
  * we reserve additional tranche IDs for builtin tranches not included in
  * the set of individual LWLocks.  A call to LWLockNewTrancheId will never
- * return a value less than LWTRANCHE_FIRST_USER_DEFINED.
+ * return a value less than LWTRANCHE_FIRST_USER_DEFINED.  The actual list of
+ * built-in tranches is kept in lwlocklist.h.
  */
 typedef enum BuiltinTrancheIds
 {
-   LWTRANCHE_XACT_BUFFER = NUM_INDIVIDUAL_LWLOCKS,
-   LWTRANCHE_COMMITTS_BUFFER,
-   LWTRANCHE_SUBTRANS_BUFFER,
-   LWTRANCHE_MULTIXACTOFFSET_BUFFER,
-   LWTRANCHE_MULTIXACTMEMBER_BUFFER,
-   LWTRANCHE_NOTIFY_BUFFER,
-   LWTRANCHE_SERIAL_BUFFER,
-   LWTRANCHE_WAL_INSERT,
-   LWTRANCHE_BUFFER_CONTENT,
-   LWTRANCHE_REPLICATION_ORIGIN_STATE,
-   LWTRANCHE_REPLICATION_SLOT_IO,
-   LWTRANCHE_LOCK_FASTPATH,
-   LWTRANCHE_BUFFER_MAPPING,
-   LWTRANCHE_LOCK_MANAGER,
-   LWTRANCHE_PREDICATE_LOCK_MANAGER,
-   LWTRANCHE_PARALLEL_HASH_JOIN,
-   LWTRANCHE_PARALLEL_BTREE_SCAN,
-   LWTRANCHE_PARALLEL_QUERY_DSA,
-   LWTRANCHE_PER_SESSION_DSA,
-   LWTRANCHE_PER_SESSION_RECORD_TYPE,
-   LWTRANCHE_PER_SESSION_RECORD_TYPMOD,
-   LWTRANCHE_SHARED_TUPLESTORE,
-   LWTRANCHE_SHARED_TIDBITMAP,
-   LWTRANCHE_PARALLEL_APPEND,
-   LWTRANCHE_PER_XACT_PREDICATE_LIST,
-   LWTRANCHE_PGSTATS_DSA,
-   LWTRANCHE_PGSTATS_HASH,
-   LWTRANCHE_PGSTATS_DATA,
-   LWTRANCHE_LAUNCHER_DSA,
-   LWTRANCHE_LAUNCHER_HASH,
-   LWTRANCHE_DSM_REGISTRY_DSA,
-   LWTRANCHE_DSM_REGISTRY_HASH,
-   LWTRANCHE_COMMITTS_SLRU,
-   LWTRANCHE_MULTIXACTMEMBER_SLRU,
-   LWTRANCHE_MULTIXACTOFFSET_SLRU,
-   LWTRANCHE_NOTIFY_SLRU,
-   LWTRANCHE_SERIAL_SLRU,
-   LWTRANCHE_SUBTRANS_SLRU,
-   LWTRANCHE_XACT_SLRU,
-   LWTRANCHE_PARALLEL_VACUUM_DSA,
-   LWTRANCHE_AIO_URING_COMPLETION,
+   /*
+    * LWTRANCHE_INVALID is an unused value that only exists to initialize the
+    * rest of the tranches to appropriate values.
+    */
+   LWTRANCHE_INVALID = NUM_INDIVIDUAL_LWLOCKS - 1,
+
+#define PG_LWLOCK(id, name)
+#define PG_LWLOCKTRANCHE(id, name) LWTRANCHE_##id,
+#include "storage/lwlocklist.h"
+#undef PG_LWLOCK
+#undef PG_LWLOCKTRANCHE
+
    LWTRANCHE_FIRST_USER_DEFINED,
 }          BuiltinTrancheIds;
 
index a9681738146e1d83e5501fbb752513791464c427..208d2e3a8ed9eef6342bd0df5b3ff03f0d032206 100644 (file)
@@ -2,9 +2,10 @@
  *
  * lwlocklist.h
  *
- * The predefined LWLock list is kept in its own source file for use by
- * automatic tools.  The exact representation of a keyword is determined by
- * the PG_LWLOCK macro, which is not defined in this file; it can be
+ * The list of predefined LWLocks and built-in LWLock tranches is kept in
+ * its own source file for use by automatic tools.  The exact
+ * representation of a keyword is determined by the PG_LWLOCK and
+ * PG_LWLOCKTRANCHE macros, which are not defined in this file; they can be
  * defined by the caller for special purposes.
  *
  * Also, generate-lwlocknames.pl processes this file to create lwlocknames.h.
@@ -84,3 +85,53 @@ PG_LWLOCK(50, DSMRegistry)
 PG_LWLOCK(51, InjectionPoint)
 PG_LWLOCK(52, SerialControl)
 PG_LWLOCK(53, AioWorkerSubmissionQueue)
+
+/*
+ * There also exist several built-in LWLock tranches.  As with the predefined
+ * LWLocks, be sure to update the WaitEventLWLock section of
+ * src/backend/utils/activity/wait_event_names.txt when modifying this list.
+ *
+ * Note that the IDs here (the first value) don't include the LWTRANCHE_
+ * prefix.  It's added elsewhere.
+ */
+PG_LWLOCKTRANCHE(XACT_BUFFER, XactBuffer)
+PG_LWLOCKTRANCHE(COMMITTS_BUFFER, CommitTsBuffer)
+PG_LWLOCKTRANCHE(SUBTRANS_BUFFER, SubtransBuffer)
+PG_LWLOCKTRANCHE(MULTIXACTOFFSET_BUFFER, MultiXactOffsetBuffer)
+PG_LWLOCKTRANCHE(MULTIXACTMEMBER_BUFFER, MultiXactMemberBuffer)
+PG_LWLOCKTRANCHE(NOTIFY_BUFFER, NotifyBuffer)
+PG_LWLOCKTRANCHE(SERIAL_BUFFER, SerialBuffer)
+PG_LWLOCKTRANCHE(WAL_INSERT, WALInsert)
+PG_LWLOCKTRANCHE(BUFFER_CONTENT, BufferContent)
+PG_LWLOCKTRANCHE(REPLICATION_ORIGIN_STATE, ReplicationOriginState)
+PG_LWLOCKTRANCHE(REPLICATION_SLOT_IO, ReplicationSlotIO)
+PG_LWLOCKTRANCHE(LOCK_FASTPATH, LockFastPath)
+PG_LWLOCKTRANCHE(BUFFER_MAPPING, BufferMapping)
+PG_LWLOCKTRANCHE(LOCK_MANAGER, LockManager)
+PG_LWLOCKTRANCHE(PREDICATE_LOCK_MANAGER, PredicateLockManager)
+PG_LWLOCKTRANCHE(PARALLEL_HASH_JOIN, ParallelHashJoin)
+PG_LWLOCKTRANCHE(PARALLEL_BTREE_SCAN, ParallelBtreeScan)
+PG_LWLOCKTRANCHE(PARALLEL_QUERY_DSA, ParallelQueryDSA)
+PG_LWLOCKTRANCHE(PER_SESSION_DSA, PerSessionDSA)
+PG_LWLOCKTRANCHE(PER_SESSION_RECORD_TYPE, PerSessionRecordType)
+PG_LWLOCKTRANCHE(PER_SESSION_RECORD_TYPMOD, PerSessionRecordTypmod)
+PG_LWLOCKTRANCHE(SHARED_TUPLESTORE, SharedTupleStore)
+PG_LWLOCKTRANCHE(SHARED_TIDBITMAP, SharedTidBitmap)
+PG_LWLOCKTRANCHE(PARALLEL_APPEND, ParallelAppend)
+PG_LWLOCKTRANCHE(PER_XACT_PREDICATE_LIST, PerXactPredicateList)
+PG_LWLOCKTRANCHE(PGSTATS_DSA, PgStatsDSA)
+PG_LWLOCKTRANCHE(PGSTATS_HASH, PgStatsHash)
+PG_LWLOCKTRANCHE(PGSTATS_DATA, PgStatsData)
+PG_LWLOCKTRANCHE(LAUNCHER_DSA, LogicalRepLauncherDSA)
+PG_LWLOCKTRANCHE(LAUNCHER_HASH, LogicalRepLauncherHash)
+PG_LWLOCKTRANCHE(DSM_REGISTRY_DSA, DSMRegistryDSA)
+PG_LWLOCKTRANCHE(DSM_REGISTRY_HASH, DSMRegistryHash)
+PG_LWLOCKTRANCHE(COMMITTS_SLRU, CommitTsSLRU)
+PG_LWLOCKTRANCHE(MULTIXACTOFFSET_SLRU, MultiXactOffsetSLRU)
+PG_LWLOCKTRANCHE(MULTIXACTMEMBER_SLRU, MultiXactMemberSLRU)
+PG_LWLOCKTRANCHE(NOTIFY_SLRU, NotifySLRU)
+PG_LWLOCKTRANCHE(SERIAL_SLRU, SerialSLRU)
+PG_LWLOCKTRANCHE(SUBTRANS_SLRU, SubtransSLRU)
+PG_LWLOCKTRANCHE(XACT_SLRU, XactSLRU)
+PG_LWLOCKTRANCHE(PARALLEL_VACUUM_DSA, ParallelVacuumDSA)
+PG_LWLOCKTRANCHE(AIO_URING_COMPLETION, AioUringCompletion)