From c1772ad9225641c921545b35c84ee478c326b95e Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Thu, 4 Feb 2016 16:43:04 -0500 Subject: [PATCH] Change the way that LWLocks for extensions are allocated. The previous RequestAddinLWLocks() method had several disadvantages. First, the locks would be in the main tranche; we've recently decided that it's useful for LWLocks used for separate purposes to have separate tranche IDs. Second, there wasn't any correlation between what code called RequestAddinLWLocks() and what code called LWLockAssign(); when multiple modules are in use, it could become quite difficult to troubleshoot problems where LWLockAssign() ran out of locks. To fix, create a concept of named LWLock tranches which can be used either by extension or by core code. Amit Kapila and Robert Haas --- .../pg_stat_statements/pg_stat_statements.c | 4 +- doc/src/sgml/xfunc.sgml | 9 +- src/backend/postmaster/postmaster.c | 6 + src/backend/storage/lmgr/lwlock.c | 212 +++++++++++++++--- src/include/pg_config_manual.h | 5 - src/include/storage/lwlock.h | 22 +- src/tools/pgindent/typedefs.list | 2 + 7 files changed, 210 insertions(+), 50 deletions(-) diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 9673fe0531..dffc4778fa 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -404,7 +404,7 @@ _PG_init(void) * resources in pgss_shmem_startup(). */ RequestAddinShmemSpace(pgss_memsize()); - RequestAddinLWLocks(1); + RequestNamedLWLockTranche("pg_stat_statements", 1); /* * Install hooks. @@ -480,7 +480,7 @@ pgss_shmem_startup(void) if (!found) { /* First time through ... */ - pgss->lock = LWLockAssign(); + pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock; pgss->cur_median_usage = ASSUMED_MEDIAN_INIT; pgss->mean_query_len = ASSUMED_LENGTH_INIT; SpinLockInit(&pgss->mutex); diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 1186d9ca58..d8d2e9e490 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -3335,9 +3335,12 @@ void RequestAddinShmemSpace(int size) LWLocks are reserved by calling: -void RequestAddinLWLocks(int n) +void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) - from _PG_init. + from _PG_init. This will ensure that an array of + num_lwlocks LWLocks is available under the name + tranche_name. Use GetNamedLWLockTranche + to get a pointer to this array. To avoid possible race-conditions, each backend should use the LWLock @@ -3356,7 +3359,7 @@ if (!ptr) { initialize contents of shmem area; acquire any requested LWLocks using: - ptr->mylockid = LWLockAssign(); + ptr->locks = GetNamedLWLockTranche("my tranche name"); } LWLockRelease(AddinShmemInitLock); } diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index d983a50ee1..b16fc28a27 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -485,6 +485,8 @@ typedef struct #ifndef HAVE_SPINLOCKS PGSemaphore SpinlockSemaArray; #endif + int NamedLWLockTrancheRequests; + NamedLWLockTranche *NamedLWLockTrancheArray; LWLockPadded *MainLWLockArray; slock_t *ProcStructLock; PROC_HDR *ProcGlobal; @@ -5800,6 +5802,8 @@ save_backend_variables(BackendParameters *param, Port *port, #ifndef HAVE_SPINLOCKS param->SpinlockSemaArray = SpinlockSemaArray; #endif + param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests; + param->NamedLWLockTrancheArray = NamedLWLockTrancheArray; param->MainLWLockArray = MainLWLockArray; param->ProcStructLock = ProcStructLock; param->ProcGlobal = ProcGlobal; @@ -6031,6 +6035,8 @@ restore_backend_variables(BackendParameters *param, Port *port) #ifndef HAVE_SPINLOCKS SpinlockSemaArray = param->SpinlockSemaArray; #endif + NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests; + NamedLWLockTrancheArray = param->NamedLWLockTrancheArray; MainLWLockArray = param->MainLWLockArray; ProcStructLock = param->ProcStructLock; ProcGlobal = param->ProcGlobal; diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index d087139c28..5c68473c62 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -144,8 +144,20 @@ typedef struct LWLockHandle static int num_held_lwlocks = 0; static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS]; -static int lock_addin_request = 0; -static bool lock_addin_request_allowed = true; +/* struct representing the LWLock tranche request for named tranche */ +typedef struct NamedLWLockTrancheRequest +{ + char tranche_name[NAMEDATALEN]; + int num_lwlocks; +} NamedLWLockTrancheRequest; + +NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL; +static int NamedLWLockTrancheRequestsAllocated = 0; +int NamedLWLockTrancheRequests = 0; + +NamedLWLockTranche *NamedLWLockTrancheArray = NULL; + +static bool lock_named_request_allowed = true; #ifdef LWLOCK_STATS typedef struct lwlock_stats_key @@ -335,6 +347,22 @@ get_lwlock_stats_entry(LWLock *lock) #endif /* LWLOCK_STATS */ +/* + * Compute number of LWLocks required by named tranches. These will be + * allocated in the main array. + */ +static int +NumLWLocksByNamedTranches(void) +{ + int numLocks = 0; + int i; + + for (i = 0; i < NamedLWLockTrancheRequests; i++) + numLocks += NamedLWLockTrancheRequestArray[i].num_lwlocks; + + return numLocks; +} + /* * Compute number of LWLocks to allocate in the main array. */ @@ -353,64 +381,49 @@ NumLWLocks(void) /* Predefined LWLocks */ numLocks = NUM_FIXED_LWLOCKS; - /* - * Add any requested by loadable modules; for backwards-compatibility - * reasons, allocate at least NUM_USER_DEFINED_LWLOCKS of them even if - * there are no explicit requests. - */ - lock_addin_request_allowed = false; - numLocks += Max(lock_addin_request, NUM_USER_DEFINED_LWLOCKS); + /* Disallow named LWLocks' requests after startup */ + lock_named_request_allowed = false; return numLocks; } - -/* - * RequestAddinLWLocks - * Request that extra LWLocks be allocated for use by - * a loadable module. - * - * This is only useful if called from the _PG_init hook of a library that - * is loaded into the postmaster via shared_preload_libraries. Once - * shared memory has been allocated, calls will be ignored. (We could - * raise an error, but it seems better to make it a no-op, so that - * libraries containing such calls can be reloaded if needed.) - */ -void -RequestAddinLWLocks(int n) -{ - if (IsUnderPostmaster || !lock_addin_request_allowed) - return; /* too late */ - lock_addin_request += n; -} - - /* - * Compute shmem space needed for LWLocks. + * Compute shmem space needed for LWLocks and named tranches. */ Size LWLockShmemSize(void) { Size size; + int i; int numLocks = NumLWLocks(); + numLocks += NumLWLocksByNamedTranches(); + /* Space for the LWLock array. */ size = mul_size(numLocks, sizeof(LWLockPadded)); /* Space for dynamic allocation counter, plus room for alignment. */ size = add_size(size, 3 * sizeof(int) + LWLOCK_PADDED_SIZE); + /* space for named tranches. */ + size = add_size(size, mul_size(NamedLWLockTrancheRequests, sizeof(NamedLWLockTranche))); + + /* space for name of each tranche. */ + for (i = 0; i < NamedLWLockTrancheRequests; i++) + size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1); + return size; } - /* - * Allocate shmem space for the main LWLock array and initialize it. We also - * register the main tranch here. + * Allocate shmem space for the main LWLock array and named tranches and + * initialize it. We also register the main and named tranche here. */ void CreateLWLocks(void) { + int i; + StaticAssertExpr(LW_VAL_EXCLUSIVE > (uint32) MAX_BACKENDS, "MAX_BACKENDS too big for lwlock.c"); @@ -421,11 +434,13 @@ CreateLWLocks(void) if (!IsUnderPostmaster) { int numLocks = NumLWLocks(); + int numNamedLocks = NumLWLocksByNamedTranches(); Size spaceLocks = LWLockShmemSize(); LWLockPadded *lock; int *LWLockCounter; char *ptr; int id; + int j; /* Allocate space */ ptr = (char *) ShmemAlloc(spaceLocks); @@ -438,7 +453,7 @@ CreateLWLocks(void) MainLWLockArray = (LWLockPadded *) ptr; - /* Initialize all LWLocks in main array */ + /* Initialize all fixed LWLocks in main array */ for (id = 0, lock = MainLWLockArray; id < numLocks; id++, lock++) LWLockInitialize(&lock->lock, LWTRANCHE_MAIN); @@ -453,6 +468,40 @@ CreateLWLocks(void) LWLockCounter[0] = NUM_FIXED_LWLOCKS; LWLockCounter[1] = numLocks; LWLockCounter[2] = LWTRANCHE_FIRST_USER_DEFINED; + + /* Initialize named tranches. */ + if (NamedLWLockTrancheRequests > 0) + { + char *trancheNames; + + NamedLWLockTrancheArray = (NamedLWLockTranche *) + &MainLWLockArray[numLocks + numNamedLocks]; + + trancheNames = (char *) NamedLWLockTrancheArray + + (NamedLWLockTrancheRequests * sizeof(NamedLWLockTranche)); + lock = &MainLWLockArray[numLocks]; + + for (i = 0; i < NamedLWLockTrancheRequests; i++) + { + NamedLWLockTrancheRequest *request; + NamedLWLockTranche *tranche; + char *name; + + request = &NamedLWLockTrancheRequestArray[i]; + tranche = &NamedLWLockTrancheArray[i]; + + name = trancheNames; + trancheNames += strlen(request->tranche_name) + 1; + strcpy(name, request->tranche_name); + tranche->lwLockTranche.name = name; + tranche->trancheId = LWLockNewTrancheId(); + tranche->lwLockTranche.array_base = lock; + tranche->lwLockTranche.array_stride = sizeof(LWLockPadded); + + for (j = 0; j < request->num_lwlocks; j++, lock++) + LWLockInitialize(&lock->lock, tranche->trancheId); + } + } } if (LWLockTrancheArray == NULL) @@ -468,6 +517,11 @@ CreateLWLocks(void) MainLWLockTranche.array_base = MainLWLockArray; MainLWLockTranche.array_stride = sizeof(LWLockPadded); LWLockRegisterTranche(LWTRANCHE_MAIN, &MainLWLockTranche); + + /* Register named tranches. */ + for (i = 0; i < NamedLWLockTrancheRequests; i++) + LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId, + &NamedLWLockTrancheArray[i].lwLockTranche); } /* @@ -507,6 +561,45 @@ LWLockAssign(void) return result; } +/* + * GetNamedLWLockTranche - returns the base address of LWLock from the + * specified tranche. + * + * Caller needs to retrieve the requested number of LWLocks starting from + * the base lock address returned by this API. This can be used for + * tranches that are requested by using RequestNamedLWLockTranche() API. + */ +LWLockPadded * +GetNamedLWLockTranche(const char *tranche_name) +{ + int lock_pos; + int i; + int *LWLockCounter; + + LWLockCounter = (int *) ((char *) MainLWLockArray - 3 * sizeof(int)); + + /* + * Obtain the position of base address of LWLock belonging to requested + * tranche_name in MainLWLockArray. LWLocks for named tranches are placed + * in MainLWLockArray after LWLocks specified by LWLockCounter[1]. + */ + lock_pos = LWLockCounter[1]; + for (i = 0; i < NamedLWLockTrancheRequests; i++) + { + if (strcmp(NamedLWLockTrancheRequestArray[i].tranche_name, + tranche_name) == 0) + return &MainLWLockArray[lock_pos]; + + lock_pos += NamedLWLockTrancheRequestArray[i].num_lwlocks; + } + + if (i >= NamedLWLockTrancheRequests) + elog(ERROR, "requested tranche is not registered"); + + /* just to keep compiler quiet */ + return NULL; +} + /* * Allocate a new tranche ID. */ @@ -551,6 +644,55 @@ LWLockRegisterTranche(int tranche_id, LWLockTranche *tranche) LWLockTrancheArray[tranche_id] = tranche; } +/* + * RequestNamedLWLockTranche + * Request that extra LWLocks be allocated during postmaster + * startup. + * + * This is only useful for extensions if called from the _PG_init hook + * of a library that is loaded into the postmaster via + * shared_preload_libraries. Once shared memory has been allocated, calls + * will be ignored. (We could raise an error, but it seems better to make + * it a no-op, so that libraries containing such calls can be reloaded if + * needed.) + */ +void +RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) +{ + NamedLWLockTrancheRequest *request; + + if (IsUnderPostmaster || !lock_named_request_allowed) + return; /* too late */ + + if (NamedLWLockTrancheRequestArray == NULL) + { + NamedLWLockTrancheRequestsAllocated = 16; + NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) + MemoryContextAlloc(TopMemoryContext, + NamedLWLockTrancheRequestsAllocated + * sizeof(NamedLWLockTrancheRequest)); + } + + if (NamedLWLockTrancheRequests >= NamedLWLockTrancheRequestsAllocated) + { + int i = NamedLWLockTrancheRequestsAllocated; + + while (i <= NamedLWLockTrancheRequests) + i *= 2; + + NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) + repalloc(NamedLWLockTrancheRequestArray, + i * sizeof(NamedLWLockTrancheRequest)); + NamedLWLockTrancheRequestsAllocated = i; + } + + request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests]; + Assert(strlen(tranche_name) + 1 < NAMEDATALEN); + StrNCpy(request->tranche_name, tranche_name, NAMEDATALEN); + request->num_lwlocks = num_lwlocks; + NamedLWLockTrancheRequests++; +} + /* * LWLockInitialize - initialize a new lwlock; it's initially unlocked */ diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index 383809bf5d..ef895211da 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -51,11 +51,6 @@ #define SEQ_MAXVALUE PG_INT64_MAX #define SEQ_MINVALUE (-SEQ_MAXVALUE) -/* - * Number of spare LWLocks to allocate for user-defined add-on code. - */ -#define NUM_USER_DEFINED_LWLOCKS 4 - /* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 613df19ad6..b2bdbbaf1b 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -119,6 +119,16 @@ typedef union LWLockMinimallyPadded extern PGDLLIMPORT LWLockPadded *MainLWLockArray; extern char *MainLWLockNames[]; +/* struct for storing named tranche information */ +typedef struct NamedLWLockTranche +{ + LWLockTranche lwLockTranche; + int trancheId; +} NamedLWLockTranche; + +extern PGDLLIMPORT NamedLWLockTranche *NamedLWLockTrancheArray; +extern PGDLLIMPORT int NamedLWLockTrancheRequests; + /* Names for fixed lwlocks */ #include "storage/lwlocknames.h" @@ -178,12 +188,14 @@ extern void CreateLWLocks(void); extern void InitLWLockAccess(void); /* - * The traditional method for obtaining an lwlock for use by an extension is - * to call RequestAddinLWLocks() during postmaster startup; this will reserve - * space for the indicated number of locks in MainLWLockArray. Subsequently, - * a lock can be allocated using LWLockAssign. + * Extensions (or core code) can obtain an LWLocks by calling + * RequestNamedLWLockTranche() during postmaster startup. Subsequently, + * call GetNamedLWLockTranche() and to obtain a pointer to an array + * containing the number of LWLocks requested. */ -extern void RequestAddinLWLocks(int n); +extern void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks); +extern LWLockPadded *GetNamedLWLockTranche(const char *tranche_name); + extern LWLock *LWLockAssign(void); /* diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 963d865114..d96896b4c2 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1008,10 +1008,12 @@ LSEG LVRelStats LWLock LWLockHandle +NamedLWLockTranche LWLockMinimallyPadded LWLockMode LWLockPadded LWLockTranche +NamedLWLockTrancheRequest LabelProvider LargeObjectDesc Latch -- 2.40.0