From: Heikki Linnakangas Date: Thu, 26 Mar 2026 21:47:22 +0000 (+0200) Subject: Refactor how user-defined LWLock tranches are stored in shmem X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d6eba30a245cd258e0b62753088202284dc59dc0;p=thirdparty%2Fpostgresql.git Refactor how user-defined LWLock tranches are stored in shmem Merge the LWLockTranches and NamedLWLockTrancheRequest data structures in shared memory into one array of user-defined tranches. The NamedLWLockTrancheRequest list is now only used in postmaster, to hold the requests until shared memory is initialized. Introduce a C struct, LWLockTranches, to hold all the different fields kept in shared memory. This gives an easier overview of what are all the things kept in shared memory. Previously, we had separate pointers for LWLockTrancheNames, LWLockCounter and the (shared memory copy of) NamedLWLockTrancheRequestArray. Reviewed-by: Nathan Bossart Discussion: https://www.postgresql.org/message-id/47aaf57e-1b7b-4e12-bda2-0316081ff50e@iki.fi --- diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 30357845729..8c134eaca88 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -99,10 +99,7 @@ typedef struct #ifdef USE_INJECTION_POINTS struct InjectionPointsCtl *ActiveInjectionPoints; #endif - int NamedLWLockTrancheRequests; - NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray; - char **LWLockTrancheNames; - int *LWLockCounter; + LWLockTrancheShmemData *LWLockTranches; LWLockPadded *MainLWLockArray; PROC_HDR *ProcGlobal; PGPROC *AuxiliaryProcs; @@ -729,10 +726,7 @@ save_backend_variables(BackendParameters *param, param->ActiveInjectionPoints = ActiveInjectionPoints; #endif - param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests; - param->NamedLWLockTrancheRequestArray = NamedLWLockTrancheRequestArray; - param->LWLockTrancheNames = LWLockTrancheNames; - param->LWLockCounter = LWLockCounter; + param->LWLockTranches = LWLockTranches; param->MainLWLockArray = MainLWLockArray; param->ProcGlobal = ProcGlobal; param->AuxiliaryProcs = AuxiliaryProcs; @@ -988,10 +982,7 @@ restore_backend_variables(BackendParameters *param) ActiveInjectionPoints = param->ActiveInjectionPoints; #endif - NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests; - NamedLWLockTrancheRequestArray = param->NamedLWLockTrancheRequestArray; - LWLockTrancheNames = param->LWLockTrancheNames; - LWLockCounter = param->LWLockCounter; + LWLockTranches = param->LWLockTranches; MainLWLockArray = param->MainLWLockArray; ProcGlobal = param->ProcGlobal; AuxiliaryProcs = param->AuxiliaryProcs; diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 0980fcc55f9..55b200a5e7d 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -127,8 +127,8 @@ 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 LWLockNewTrancheId. These names are stored in shared memory and can be - * accessed via LWLockTrancheNames. + * or LWLockNewTrancheId. These are stored in shared memory and can be + * accessed via LWLockTranches. * * 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. @@ -145,15 +145,6 @@ StaticAssertDecl(lengthof(BuiltinTrancheNames) == LWTRANCHE_FIRST_USER_DEFINED, "missing entries in BuiltinTrancheNames[]"); -/* - * This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED, and - * points to the shared memory locations of the names of all - * dynamically-created tranches. Backends inherit the pointer by fork from the - * postmaster (except in the EXEC_BACKEND case, where we have special measures - * to pass it down). - */ -char **LWLockTrancheNames = NULL; - /* * 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, @@ -178,33 +169,49 @@ typedef struct LWLockHandle static int num_held_lwlocks = 0; static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS]; -/* struct representing the LWLock tranche request for named tranche */ -typedef struct NamedLWLockTrancheRequest -{ - char tranche_name[NAMEDATALEN]; - int num_lwlocks; -} NamedLWLockTrancheRequest; +/* Maximum number of LWLock tranches that can be assigned by extensions */ +#define MAX_USER_DEFINED_TRANCHES 256 /* - * NamedLWLockTrancheRequests is the valid length of the request array. These - * variables are non-static so that launch_backend.c can copy them to child - * processes in EXEC_BACKEND builds. + * Shared memory structure holding user-defined tranches. */ -int NamedLWLockTrancheRequests = 0; -NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL; +typedef struct LWLockTrancheShmemData +{ + /* This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED */ + struct + { + char name[NAMEDATALEN]; -/* postmaster's local copy of the request array */ -static NamedLWLockTrancheRequest *LocalNamedLWLockTrancheRequestArray = NULL; + /* + * Index of the tranche's locks in MainLWLockArray if this tranche was + * allocated with RequestNamedLWLockTranche(), or -1 if the tranche + * was allocated with LWLockNewTrancheId() + */ + int main_array_idx; + } user_defined[MAX_USER_DEFINED_TRANCHES]; -/* shared memory counter of registered tranches */ -int *LWLockCounter = NULL; + int num_user_defined; /* 'user_defined' entries in use */ +} LWLockTrancheShmemData; -/* backend-local counter of registered tranches */ -static int LocalLWLockCounter; +LWLockTrancheShmemData *LWLockTranches; -#define MAX_USER_DEFINED_TRANCHES 256 +/* backend-local copy of NamedLWLockTranches->num_user_defined */ +static int LocalNumUserDefinedTranches; -static void InitializeLWLocks(void); +/* + * NamedLWLockTrancheRequests is a list of tranches requested with + * RequestNamedLWLockTranche(). It is only valid in the postmaster; after + * startup the tranches are tracked in LWLockTranches in shared memory. + */ +typedef struct NamedLWLockTrancheRequest +{ + char tranche_name[NAMEDATALEN]; + int num_lwlocks; +} NamedLWLockTrancheRequest; + +static List *NamedLWLockTrancheRequests = NIL; + +static void InitializeLWLocks(int numLocks); static inline void LWLockReportWaitStart(LWLock *lock); static inline void LWLockReportWaitEnd(void); static const char *GetLWTrancheName(uint16 trancheId); @@ -376,17 +383,18 @@ get_lwlock_stats_entry(LWLock *lock) /* - * Compute number of LWLocks required by named tranches. These will be - * allocated in the main array. + * Compute number of LWLocks required by user-defined tranches requested with + * RequestNamedLWLockTranche(). These will be allocated in the main array. */ static int NumLWLocksForNamedTranches(void) { int numLocks = 0; - int i; - for (i = 0; i < NamedLWLockTrancheRequests; i++) - numLocks += NamedLWLockTrancheRequestArray[i].num_lwlocks; + foreach_ptr(NamedLWLockTrancheRequest, request, NamedLWLockTrancheRequests) + { + numLocks += request->num_lwlocks; + } return numLocks; } @@ -399,37 +407,13 @@ Size LWLockShmemSize(void) { Size size; - int numLocks = NUM_FIXED_LWLOCKS; - - /* - * If re-initializing shared memory, the request array will no longer be - * accessible, so switch to the copy in postmaster's local memory. We'll - * copy it back into shared memory later when CreateLWLocks() is called - * again. - */ - if (LocalNamedLWLockTrancheRequestArray) - NamedLWLockTrancheRequestArray = LocalNamedLWLockTrancheRequestArray; - - /* Calculate total number of locks needed in the main array. */ - numLocks += NumLWLocksForNamedTranches(); - - /* Space for dynamic allocation counter. */ - size = MAXALIGN(sizeof(int)); + int numLocks; - /* Space for user-defined tranches. */ - size = add_size(size, mul_size(MAX_USER_DEFINED_TRANCHES, sizeof(char *))); - size = add_size(size, mul_size(MAX_USER_DEFINED_TRANCHES, NAMEDATALEN)); + /* Space for user-defined tranches */ + size = sizeof(LWLockTrancheShmemData); - /* - * Make space for named tranche requests. This is done for the benefit of - * EXEC_BACKEND builds, which otherwise wouldn't be able to call - * GetNamedLWLockTranche() outside postmaster. - */ - size = add_size(size, mul_size(NamedLWLockTrancheRequests, - sizeof(NamedLWLockTrancheRequest))); - - /* Space for the LWLock array, plus room for cache line alignment. */ - size = add_size(size, LWLOCK_PADDED_SIZE); + /* Space for the LWLock array */ + numLocks = NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches(); size = add_size(size, mul_size(numLocks, sizeof(LWLockPadded))); return size; @@ -442,54 +426,21 @@ LWLockShmemSize(void) void CreateLWLocks(void) { + int numLocks; + if (!IsUnderPostmaster) { - Size spaceLocks = LWLockShmemSize(); - char *ptr; - - /* Allocate space */ - ptr = (char *) ShmemAlloc(spaceLocks); + /* Allocate space for LWLockTranches */ + LWLockTranches = (LWLockTrancheShmemData *) + ShmemAlloc(sizeof(LWLockTrancheShmemData)); /* Initialize the dynamic-allocation counter for tranches */ - LWLockCounter = (int *) ptr; - *LWLockCounter = LWTRANCHE_FIRST_USER_DEFINED; - ptr += MAXALIGN(sizeof(int)); - - /* Initialize user-defined tranche names */ - LWLockTrancheNames = (char **) ptr; - ptr += MAX_USER_DEFINED_TRANCHES * sizeof(char *); - for (int i = 0; i < MAX_USER_DEFINED_TRANCHES; i++) - { - LWLockTrancheNames[i] = ptr; - ptr += NAMEDATALEN; - } - - /* - * Move named tranche requests to shared memory. This is done for the - * benefit of EXEC_BACKEND builds, which otherwise wouldn't be able to - * call GetNamedLWLockTranche() outside postmaster. - */ - if (NamedLWLockTrancheRequests > 0) - { - /* - * Save the pointer to the request array in postmaster's local - * memory. We'll need it if we ever need to re-initialize shared - * memory after a crash. - */ - LocalNamedLWLockTrancheRequestArray = NamedLWLockTrancheRequestArray; - - memcpy(ptr, NamedLWLockTrancheRequestArray, - NamedLWLockTrancheRequests * sizeof(NamedLWLockTrancheRequest)); - NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) ptr; - ptr += NamedLWLockTrancheRequests * sizeof(NamedLWLockTrancheRequest); - } - - /* Ensure desired alignment of LWLock array */ - ptr += LWLOCK_PADDED_SIZE - ((uintptr_t) ptr) % LWLOCK_PADDED_SIZE; - MainLWLockArray = (LWLockPadded *) ptr; + LWLockTranches->num_user_defined = 0; - /* Initialize all LWLocks */ - InitializeLWLocks(); + /* Allocate and initialize the main array */ + numLocks = NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches(); + MainLWLockArray = (LWLockPadded *) ShmemAlloc(numLocks * sizeof(LWLockPadded)); + InitializeLWLocks(numLocks); } } @@ -497,52 +448,49 @@ CreateLWLocks(void) * Initialize LWLocks that are fixed and those belonging to named tranches. */ static void -InitializeLWLocks(void) +InitializeLWLocks(int numLocks) { - int id; - int i; - int j; - LWLockPadded *lock; + int pos = 0; /* Initialize all individual LWLocks in main array */ - for (id = 0, lock = MainLWLockArray; id < NUM_INDIVIDUAL_LWLOCKS; id++, lock++) - LWLockInitialize(&lock->lock, id); + for (int id = 0; id < NUM_INDIVIDUAL_LWLOCKS; id++) + LWLockInitialize(&MainLWLockArray[pos++].lock, id); /* Initialize buffer mapping LWLocks in main array */ - lock = MainLWLockArray + BUFFER_MAPPING_LWLOCK_OFFSET; - for (id = 0; id < NUM_BUFFER_PARTITIONS; id++, lock++) - LWLockInitialize(&lock->lock, LWTRANCHE_BUFFER_MAPPING); + Assert(pos == BUFFER_MAPPING_LWLOCK_OFFSET); + for (int i = 0; i < NUM_BUFFER_PARTITIONS; i++) + LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_BUFFER_MAPPING); /* Initialize lmgrs' LWLocks in main array */ - lock = MainLWLockArray + LOCK_MANAGER_LWLOCK_OFFSET; - for (id = 0; id < NUM_LOCK_PARTITIONS; id++, lock++) - LWLockInitialize(&lock->lock, LWTRANCHE_LOCK_MANAGER); + Assert(pos == LOCK_MANAGER_LWLOCK_OFFSET); + for (int i = 0; i < NUM_LOCK_PARTITIONS; i++) + LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_LOCK_MANAGER); /* Initialize predicate lmgrs' LWLocks in main array */ - lock = MainLWLockArray + PREDICATELOCK_MANAGER_LWLOCK_OFFSET; - for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++) - LWLockInitialize(&lock->lock, LWTRANCHE_PREDICATE_LOCK_MANAGER); + Assert(pos == PREDICATELOCK_MANAGER_LWLOCK_OFFSET); + for (int i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++) + LWLockInitialize(&MainLWLockArray[pos++].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. */ - if (NamedLWLockTrancheRequests > 0) + Assert(pos == NUM_FIXED_LWLOCKS); + foreach_ptr(NamedLWLockTrancheRequest, request, NamedLWLockTrancheRequests) { - lock = &MainLWLockArray[NUM_FIXED_LWLOCKS]; - - for (i = 0; i < NamedLWLockTrancheRequests; i++) - { - NamedLWLockTrancheRequest *request; - int tranche; + int idx = (LWLockTranches->num_user_defined++); - request = &NamedLWLockTrancheRequestArray[i]; - tranche = LWLockNewTrancheId(request->tranche_name); + strlcpy(LWLockTranches->user_defined[idx].name, + request->tranche_name, + NAMEDATALEN); + LWLockTranches->user_defined[idx].main_array_idx = pos; - for (j = 0; j < request->num_lwlocks; j++, lock++) - LWLockInitialize(&lock->lock, tranche); - } + for (int i = 0; i < request->num_lwlocks; i++) + LWLockInitialize(&MainLWLockArray[pos++].lock, LWTRANCHE_FIRST_USER_DEFINED + idx); } + + /* Cross-check that we agree on the total size with the caller */ + Assert(pos == numLocks); } /* @@ -567,22 +515,31 @@ InitLWLockAccess(void) LWLockPadded * GetNamedLWLockTranche(const char *tranche_name) { - int lock_pos; - int i; + SpinLockAcquire(ShmemLock); + LocalNumUserDefinedTranches = LWLockTranches->num_user_defined; + SpinLockRelease(ShmemLock); /* * Obtain the position of base address of LWLock belonging to requested * tranche_name in MainLWLockArray. LWLocks for named tranches are placed * in MainLWLockArray after fixed locks. */ - lock_pos = NUM_FIXED_LWLOCKS; - for (i = 0; i < NamedLWLockTrancheRequests; i++) + for (int i = 0; i < LocalNumUserDefinedTranches; i++) { - if (strcmp(NamedLWLockTrancheRequestArray[i].tranche_name, + if (strcmp(LWLockTranches->user_defined[i].name, tranche_name) == 0) - return &MainLWLockArray[lock_pos]; + { + int lock_pos = LWLockTranches->user_defined[i].main_array_idx; - lock_pos += NamedLWLockTrancheRequestArray[i].num_lwlocks; + /* + * GetNamedLWLockTranche() should only be used for locks requested + * with RequestNamedLWLockTranche(), not those allocated with + * LWLockNewTrancheId(). + */ + if (lock_pos == -1) + elog(ERROR, "requested tranche was not registered with RequestNamedLWLockTranche()"); + return &MainLWLockArray[lock_pos]; + } } elog(ERROR, "requested tranche is not registered"); @@ -597,7 +554,7 @@ GetNamedLWLockTranche(const char *tranche_name) int LWLockNewTrancheId(const char *name) { - int result; + int idx; if (!name) ereport(ERROR, @@ -612,12 +569,12 @@ LWLockNewTrancheId(const char *name) NAMEDATALEN - 1))); /* - * We use the ShmemLock spinlock to protect LWLockCounter and - * LWLockTrancheNames. + * We use the ShmemLock spinlock to protect the counter and the tranche + * names. */ SpinLockAcquire(ShmemLock); - if (*LWLockCounter - LWTRANCHE_FIRST_USER_DEFINED >= MAX_USER_DEFINED_TRANCHES) + if (LWLockTranches->num_user_defined >= MAX_USER_DEFINED_TRANCHES) { SpinLockRelease(ShmemLock); ereport(ERROR, @@ -626,13 +583,21 @@ LWLockNewTrancheId(const char *name) MAX_USER_DEFINED_TRANCHES))); } - result = (*LWLockCounter)++; - LocalLWLockCounter = *LWLockCounter; - strlcpy(LWLockTrancheNames[result - LWTRANCHE_FIRST_USER_DEFINED], name, NAMEDATALEN); + /* Allocate an entry in the user_defined array */ + idx = (LWLockTranches->num_user_defined)++; + + /* update our local copy while we're at it */ + LocalNumUserDefinedTranches = LWLockTranches->num_user_defined; + + /* Initialize it */ + strlcpy(LWLockTranches->user_defined[idx].name, name, NAMEDATALEN); + + /* the locks are not in the main array */ + LWLockTranches->user_defined[idx].main_array_idx = -1; SpinLockRelease(ShmemLock); - return result; + return LWTRANCHE_FIRST_USER_DEFINED + idx; } /* @@ -651,7 +616,6 @@ void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) { NamedLWLockTrancheRequest *request; - static int NamedLWLockTrancheRequestsAllocated; if (!process_shmem_requests_in_progress) elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook"); @@ -668,29 +632,16 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) errdetail("LWLock tranche names must be no longer than %d bytes.", NAMEDATALEN - 1))); - if (NamedLWLockTrancheRequestArray == NULL) - { - NamedLWLockTrancheRequestsAllocated = 16; - NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) - MemoryContextAlloc(TopMemoryContext, - NamedLWLockTrancheRequestsAllocated - * sizeof(NamedLWLockTrancheRequest)); - } - - if (NamedLWLockTrancheRequests >= NamedLWLockTrancheRequestsAllocated) - { - int i = pg_nextpower2_32(NamedLWLockTrancheRequests + 1); - - NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) - repalloc(NamedLWLockTrancheRequestArray, - i * sizeof(NamedLWLockTrancheRequest)); - NamedLWLockTrancheRequestsAllocated = i; - } + if (list_length(NamedLWLockTrancheRequests) >= MAX_USER_DEFINED_TRANCHES) + ereport(ERROR, + (errmsg("maximum number of tranches already registered"), + errdetail("No more than %d tranches may be registered.", + MAX_USER_DEFINED_TRANCHES))); - request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests]; + request = MemoryContextAllocZero(PostmasterContext, sizeof(NamedLWLockTrancheRequest)); strlcpy(request->tranche_name, tranche_name, NAMEDATALEN); request->num_lwlocks = num_lwlocks; - NamedLWLockTrancheRequests++; + NamedLWLockTrancheRequests = lappend(NamedLWLockTrancheRequests, request); } /* @@ -738,34 +689,36 @@ LWLockReportWaitEnd(void) static const char * GetLWTrancheName(uint16 trancheId) { + int idx; + /* Built-in tranche or individual LWLock? */ if (trancheId < LWTRANCHE_FIRST_USER_DEFINED) return BuiltinTrancheNames[trancheId]; /* - * We only ever add new entries to LWLockTrancheNames, so most lookups can - * avoid taking the spinlock as long as the backend-local counter - * (LocalLWLockCounter) is greater than the requested tranche ID. Else, - * we need to first update the backend-local counter with ShmemLock held - * before attempting the lookup again. In practice, the latter case is - * probably rare. + * It's an extension tranche, so look in LWLockTranches->user_defined. + */ + idx = trancheId - LWTRANCHE_FIRST_USER_DEFINED; + + /* + * We only ever add new entries to LWLockTranches->user_defined, so most + * lookups can avoid taking the spinlock as long as the backend-local + * counter (LocalNumUserDefinedTranches) is greater than the requested + * tranche ID. Else, we need to first update the backend-local counter + * with ShmemLock held before attempting the lookup again. In practice, + * the latter case is probably rare. */ - if (trancheId >= LocalLWLockCounter) + if (idx >= LocalNumUserDefinedTranches) { SpinLockAcquire(ShmemLock); - LocalLWLockCounter = *LWLockCounter; + LocalNumUserDefinedTranches = LWLockTranches->num_user_defined; SpinLockRelease(ShmemLock); - if (trancheId >= LocalLWLockCounter) + if (idx >= LocalNumUserDefinedTranches) elog(ERROR, "tranche %d is not registered", trancheId); } - /* - * It's an extension tranche, so look in LWLockTrancheNames. - */ - trancheId -= LWTRANCHE_FIRST_USER_DEFINED; - - return LWLockTrancheNames[trancheId]; + return LWLockTranches->user_defined[idx].name; } /* diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 9a0290391d0..30557631eb8 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -74,12 +74,9 @@ typedef union LWLockPadded extern PGDLLIMPORT LWLockPadded *MainLWLockArray; /* forward declaration of private type for use only by lwlock.c */ -typedef struct NamedLWLockTrancheRequest NamedLWLockTrancheRequest; +typedef struct LWLockTrancheShmemData LWLockTrancheShmemData; -extern PGDLLIMPORT char **LWLockTrancheNames; -extern PGDLLIMPORT int NamedLWLockTrancheRequests; -extern PGDLLIMPORT NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray; -extern PGDLLIMPORT int *LWLockCounter; +extern PGDLLIMPORT LWLockTrancheShmemData *LWLockTranches; /* * It's a bit odd to declare NUM_BUFFER_PARTITIONS and NUM_LOCK_PARTITIONS diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index decc9f7a572..712d84128ca 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1577,6 +1577,7 @@ LWLock LWLockHandle LWLockMode LWLockPadded +LWLockTrancheShmemData LZ4F_compressionContext_t LZ4F_decompressOptions_t LZ4F_decompressionContext_t