Move dynamically-allocated LWLock tranche names to shared memory.

There are two ways for shared libraries to allocate their own
LWLock tranches.  One way is to call RequestNamedLWLockTranche() in
a shmem_request_hook, which requires the library to be loaded via
shared_preload_libraries.  The other way is to call
LWLockNewTrancheId(), which is not subject to the same
restrictions.  However, LWLockNewTrancheId() does require each
backend to store the tranche's name in backend-local memory via
LWLockRegisterTranche().  This API is a little cumbersome and leads
to things like unhelpful pg_stat_activity.wait_event values in
backends that haven't loaded the library.

This commit moves these LWLock tranche names to shared memory, thus
eliminating the need for each backend to call
LWLockRegisterTranche().  Instead, the tranche name must be
provided to LWLockNewTrancheId(), which immediately makes the name
available to all backends.  Since the tranche name array is
append-only, lookups can ordinarily avoid locking as long as their
local copy of the LWLock counter is greater than the requested
tranche ID.

One downside of this approach is that we now have a hard limit on
both the length of tranche names (NAMEDATALEN-1 bytes) and the
number of dynamically-allocated tranches (256).  Besides a limit of
NAMEDATALEN-1 bytes for tranche names registered via
RequestNamedLWLockTranche(), no such limits previously existed.  We
could avoid these new limits by using dynamic shared memory, but
the complexity involved didn't seem worth it.  We briefly
considered making the tranche limit user-configurable but
ultimately decided against that, too.  Since there is still a lot
of time left in the v19 development cycle, it's possible we will
revisit this choice.

Author: Sami Imseih <samimseih@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Rahila Syed <rahilasyed90@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/CAA5RZ0vvED3naph8My8Szv6DL4AxOVK3eTPS0qXsaKi%3DbVdW2A%40mail.gmail.com
master
Nathan Bossart 1 week ago
parent 7b0fb9f5c6
commit 38b602b028
  1. 3
      contrib/pg_prewarm/autoprewarm.c
  2. 15
      doc/src/sgml/xfunc.sgml
  3. 6
      src/backend/postmaster/launch_backend.c
  4. 12
      src/backend/storage/ipc/dsm_registry.c
  5. 212
      src/backend/storage/lmgr/lwlock.c
  6. 25
      src/include/storage/lwlock.h
  7. 6
      src/test/modules/test_dsa/test_dsa.c
  8. 3
      src/test/modules/test_dsm_registry/test_dsm_registry.c
  9. 9
      src/test/modules/test_radixtree/test_radixtree.c
  10. 6
      src/test/modules/test_slru/test_slru.c
  11. 3
      src/test/modules/test_tidstore/test_tidstore.c

@ -864,7 +864,7 @@ apw_init_state(void *ptr)
{ {
AutoPrewarmSharedState *state = (AutoPrewarmSharedState *) ptr; AutoPrewarmSharedState *state = (AutoPrewarmSharedState *) ptr;
LWLockInitialize(&state->lock, LWLockNewTrancheId()); LWLockInitialize(&state->lock, LWLockNewTrancheId("autoprewarm"));
state->bgworker_pid = InvalidPid; state->bgworker_pid = InvalidPid;
state->pid_using_dumpfile = InvalidPid; state->pid_using_dumpfile = InvalidPid;
} }
@ -883,7 +883,6 @@ apw_init_shmem(void)
sizeof(AutoPrewarmSharedState), sizeof(AutoPrewarmSharedState),
apw_init_state, apw_init_state,
&found); &found);
LWLockRegisterTranche(apw_state->lock.tranche, "autoprewarm");
return found; return found;
} }

@ -3759,7 +3759,7 @@ LWLockPadded *GetNamedLWLockTranche(const char *tranche_name)
<literal>shmem_request_hook</literal>. To do so, first allocate a <literal>shmem_request_hook</literal>. To do so, first allocate a
<literal>tranche_id</literal> by calling: <literal>tranche_id</literal> by calling:
<programlisting> <programlisting>
int LWLockNewTrancheId(void) int LWLockNewTrancheId(const char *name)
</programlisting> </programlisting>
Next, initialize each LWLock, passing the new Next, initialize each LWLock, passing the new
<literal>tranche_id</literal> as an argument: <literal>tranche_id</literal> as an argument:
@ -3777,17 +3777,8 @@ void LWLockInitialize(LWLock *lock, int tranche_id)
</para> </para>
<para> <para>
Finally, each backend using the <literal>tranche_id</literal> should A complete usage example of <function>LWLockNewTrancheId</function> and
associate it with a <literal>tranche_name</literal> by calling: <function>LWLockInitialize</function> can be found in
<programlisting>
void LWLockRegisterTranche(int tranche_id, const char *tranche_name)
</programlisting>
</para>
<para>
A complete usage example of <function>LWLockNewTrancheId</function>,
<function>LWLockInitialize</function>, and
<function>LWLockRegisterTranche</function> can be found in
<filename>contrib/pg_prewarm/autoprewarm.c</filename> in the <filename>contrib/pg_prewarm/autoprewarm.c</filename> in the
<productname>PostgreSQL</productname> source tree. <productname>PostgreSQL</productname> source tree.
</para> </para>

@ -101,7 +101,7 @@ typedef struct
struct InjectionPointsCtl *ActiveInjectionPoints; struct InjectionPointsCtl *ActiveInjectionPoints;
#endif #endif
int NamedLWLockTrancheRequests; int NamedLWLockTrancheRequests;
NamedLWLockTranche *NamedLWLockTrancheArray; char **LWLockTrancheNames;
int *LWLockCounter; int *LWLockCounter;
LWLockPadded *MainLWLockArray; LWLockPadded *MainLWLockArray;
slock_t *ProcStructLock; slock_t *ProcStructLock;
@ -761,7 +761,7 @@ save_backend_variables(BackendParameters *param,
#endif #endif
param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests; param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
param->NamedLWLockTrancheArray = NamedLWLockTrancheArray; param->LWLockTrancheNames = LWLockTrancheNames;
param->LWLockCounter = LWLockCounter; param->LWLockCounter = LWLockCounter;
param->MainLWLockArray = MainLWLockArray; param->MainLWLockArray = MainLWLockArray;
param->ProcStructLock = ProcStructLock; param->ProcStructLock = ProcStructLock;
@ -1022,7 +1022,7 @@ restore_backend_variables(BackendParameters *param)
#endif #endif
NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests; NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
NamedLWLockTrancheArray = param->NamedLWLockTrancheArray; LWLockTrancheNames = param->LWLockTrancheNames;
LWLockCounter = param->LWLockCounter; LWLockCounter = param->LWLockCounter;
MainLWLockArray = param->MainLWLockArray; MainLWLockArray = param->MainLWLockArray;
ProcStructLock = param->ProcStructLock; ProcStructLock = param->ProcStructLock;

@ -299,8 +299,7 @@ GetNamedDSA(const char *name, bool *found)
entry->type = DSMR_ENTRY_TYPE_DSA; entry->type = DSMR_ENTRY_TYPE_DSA;
/* Initialize the LWLock tranche for the DSA. */ /* Initialize the LWLock tranche for the DSA. */
state->tranche = LWLockNewTrancheId(); state->tranche = LWLockNewTrancheId(name);
LWLockRegisterTranche(state->tranche, name);
/* Initialize the DSA. */ /* Initialize the DSA. */
ret = dsa_create(state->tranche); ret = dsa_create(state->tranche);
@ -321,9 +320,6 @@ GetNamedDSA(const char *name, bool *found)
ereport(ERROR, ereport(ERROR,
(errmsg("requested DSA already attached to current process"))); (errmsg("requested DSA already attached to current process")));
/* Initialize existing LWLock tranche for the DSA. */
LWLockRegisterTranche(state->tranche, name);
/* Attach to existing DSA. */ /* Attach to existing DSA. */
ret = dsa_attach(state->handle); ret = dsa_attach(state->handle);
dsa_pin_mapping(ret); dsa_pin_mapping(ret);
@ -378,8 +374,7 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
entry->type = DSMR_ENTRY_TYPE_DSH; entry->type = DSMR_ENTRY_TYPE_DSH;
/* Initialize the LWLock tranche for the hash table. */ /* Initialize the LWLock tranche for the hash table. */
dsh_state->tranche = LWLockNewTrancheId(); dsh_state->tranche = LWLockNewTrancheId(name);
LWLockRegisterTranche(dsh_state->tranche, name);
/* Initialize the DSA for the hash table. */ /* Initialize the DSA for the hash table. */
dsa = dsa_create(dsh_state->tranche); dsa = dsa_create(dsh_state->tranche);
@ -409,9 +404,6 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
ereport(ERROR, ereport(ERROR,
(errmsg("requested DSHash already attached to current process"))); (errmsg("requested DSHash already attached to current process")));
/* Initialize existing LWLock tranche for the hash table. */
LWLockRegisterTranche(dsh_state->tranche, name);
/* Attach to existing DSA for the hash table. */ /* Attach to existing DSA for the hash table. */
dsa = dsa_attach(dsh_state->dsa_handle); dsa = dsa_attach(dsh_state->dsa_handle);
dsa_pin_mapping(dsa); dsa_pin_mapping(dsa);

@ -126,8 +126,8 @@ StaticAssertDecl((LW_VAL_EXCLUSIVE & LW_FLAG_MASK) == 0,
* in lwlocklist.h. We absorb the names of these tranches, too. * in lwlocklist.h. We absorb the names of these tranches, too.
* *
* 3. Extensions can create new tranches, via either RequestNamedLWLockTranche * 3. Extensions can create new tranches, via either RequestNamedLWLockTranche
* or LWLockRegisterTranche. The names of these that are known in the current * or LWLockNewTrancheId. These names are stored in shared memory and can be
* process appear in LWLockTrancheNames[]. * accessed via LWLockTrancheNames.
* *
* All these names are user-visible as wait event names, so choose with care * 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. * ... and do not forget to update the documentation's list of wait events.
@ -146,11 +146,12 @@ StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
/* /*
* This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED, and * This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED, and
* stores the names of all dynamically-created tranches known to the current * points to the shared memory locations of the names of all
* process. Any unused entries in the array will contain NULL. * 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).
*/ */
static const char **LWLockTrancheNames = NULL; char **LWLockTrancheNames = NULL;
static int LWLockTrancheNamesAllocated = 0;
/* /*
* This points to the main array of LWLocks in shared memory. Backends inherit * This points to the main array of LWLocks in shared memory. Backends inherit
@ -184,20 +185,22 @@ typedef struct NamedLWLockTrancheRequest
} NamedLWLockTrancheRequest; } NamedLWLockTrancheRequest;
static NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL; static NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
static int NamedLWLockTrancheRequestsAllocated = 0;
/* /*
* NamedLWLockTrancheRequests is both the valid length of the request array, * NamedLWLockTrancheRequests is the valid length of the request array. This
* and the length of the shared-memory NamedLWLockTrancheArray later on. * variable is non-static so that postmaster.c can copy them to child processes
* This variable and NamedLWLockTrancheArray are non-static so that * in EXEC_BACKEND builds.
* postmaster.c can copy them to child processes in EXEC_BACKEND builds.
*/ */
int NamedLWLockTrancheRequests = 0; int NamedLWLockTrancheRequests = 0;
/* points to data in shared memory: */ /* shared memory counter of registered tranches */
NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
int *LWLockCounter = NULL; int *LWLockCounter = NULL;
/* backend-local counter of registered tranches */
static int LocalLWLockCounter;
#define MAX_NAMED_TRANCHES 256
static void InitializeLWLocks(void); static void InitializeLWLocks(void);
static inline void LWLockReportWaitStart(LWLock *lock); static inline void LWLockReportWaitStart(LWLock *lock);
static inline void LWLockReportWaitEnd(void); static inline void LWLockReportWaitEnd(void);
@ -392,31 +395,28 @@ Size
LWLockShmemSize(void) LWLockShmemSize(void)
{ {
Size size; Size size;
int i;
int numLocks = NUM_FIXED_LWLOCKS; int numLocks = NUM_FIXED_LWLOCKS;
/* Calculate total number of locks needed in the main array. */ /* Calculate total number of locks needed in the main array. */
numLocks += NumLWLocksForNamedTranches(); numLocks += NumLWLocksForNamedTranches();
/* Space for dynamic allocation counter, plus room for alignment. */ /* Space for dynamic allocation counter. */
size = sizeof(int) + LWLOCK_PADDED_SIZE; size = MAXALIGN(sizeof(int));
/* Space for the LWLock array. */ /* Space for named tranches. */
size = add_size(size, mul_size(numLocks, sizeof(LWLockPadded))); size = add_size(size, mul_size(MAX_NAMED_TRANCHES, sizeof(char *)));
size = add_size(size, mul_size(MAX_NAMED_TRANCHES, NAMEDATALEN));
/* space for named tranches. */ /* Space for the LWLock array, plus room for cache line alignment. */
size = add_size(size, mul_size(NamedLWLockTrancheRequests, sizeof(NamedLWLockTranche))); size = add_size(size, LWLOCK_PADDED_SIZE);
size = add_size(size, mul_size(numLocks, sizeof(LWLockPadded)));
/* space for name of each tranche. */
for (i = 0; i < NamedLWLockTrancheRequests; i++)
size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);
return size; return size;
} }
/* /*
* Allocate shmem space for the main LWLock array and all tranches and * Allocate shmem space for the main LWLock array and all tranches and
* initialize it. We also register extension LWLock tranches here. * initialize it.
*/ */
void void
CreateLWLocks(void) CreateLWLocks(void)
@ -432,7 +432,16 @@ CreateLWLocks(void)
/* Initialize the dynamic-allocation counter for tranches */ /* Initialize the dynamic-allocation counter for tranches */
LWLockCounter = (int *) ptr; LWLockCounter = (int *) ptr;
*LWLockCounter = LWTRANCHE_FIRST_USER_DEFINED; *LWLockCounter = LWTRANCHE_FIRST_USER_DEFINED;
ptr += sizeof(int); ptr += MAXALIGN(sizeof(int));
/* Initialize tranche names */
LWLockTrancheNames = (char **) ptr;
ptr += MAX_NAMED_TRANCHES * sizeof(char *);
for (int i = 0; i < MAX_NAMED_TRANCHES; i++)
{
LWLockTrancheNames[i] = ptr;
ptr += NAMEDATALEN;
}
/* Ensure desired alignment of LWLock array */ /* Ensure desired alignment of LWLock array */
ptr += LWLOCK_PADDED_SIZE - ((uintptr_t) ptr) % LWLOCK_PADDED_SIZE; ptr += LWLOCK_PADDED_SIZE - ((uintptr_t) ptr) % LWLOCK_PADDED_SIZE;
@ -441,11 +450,6 @@ CreateLWLocks(void)
/* Initialize all LWLocks */ /* Initialize all LWLocks */
InitializeLWLocks(); InitializeLWLocks();
} }
/* Register named extension LWLock tranches in the current process. */
for (int i = 0; i < NamedLWLockTrancheRequests; i++)
LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
NamedLWLockTrancheArray[i].trancheName);
} }
/* /*
@ -454,7 +458,6 @@ CreateLWLocks(void)
static void static void
InitializeLWLocks(void) InitializeLWLocks(void)
{ {
int numNamedLocks = NumLWLocksForNamedTranches();
int id; int id;
int i; int i;
int j; int j;
@ -485,32 +488,18 @@ InitializeLWLocks(void)
*/ */
if (NamedLWLockTrancheRequests > 0) if (NamedLWLockTrancheRequests > 0)
{ {
char *trancheNames;
NamedLWLockTrancheArray = (NamedLWLockTranche *)
&MainLWLockArray[NUM_FIXED_LWLOCKS + numNamedLocks];
trancheNames = (char *) NamedLWLockTrancheArray +
(NamedLWLockTrancheRequests * sizeof(NamedLWLockTranche));
lock = &MainLWLockArray[NUM_FIXED_LWLOCKS]; lock = &MainLWLockArray[NUM_FIXED_LWLOCKS];
for (i = 0; i < NamedLWLockTrancheRequests; i++) for (i = 0; i < NamedLWLockTrancheRequests; i++)
{ {
NamedLWLockTrancheRequest *request; NamedLWLockTrancheRequest *request;
NamedLWLockTranche *tranche; int tranche;
char *name;
request = &NamedLWLockTrancheRequestArray[i]; request = &NamedLWLockTrancheRequestArray[i];
tranche = &NamedLWLockTrancheArray[i]; tranche = LWLockNewTrancheId(request->tranche_name);
name = trancheNames;
trancheNames += strlen(request->tranche_name) + 1;
strcpy(name, request->tranche_name);
tranche->trancheId = LWLockNewTrancheId();
tranche->trancheName = name;
for (j = 0; j < request->num_lwlocks; j++, lock++) for (j = 0; j < request->num_lwlocks; j++, lock++)
LWLockInitialize(&lock->lock, tranche->trancheId); LWLockInitialize(&lock->lock, tranche);
} }
} }
} }
@ -562,59 +551,47 @@ GetNamedLWLockTranche(const char *tranche_name)
} }
/* /*
* Allocate a new tranche ID. * Allocate a new tranche ID with the provided name.
*/ */
int int
LWLockNewTrancheId(void) LWLockNewTrancheId(const char *name)
{ {
int result; int result;
/* We use the ShmemLock spinlock to protect LWLockCounter */ if (!name)
SpinLockAcquire(ShmemLock); ereport(ERROR,
result = (*LWLockCounter)++; (errcode(ERRCODE_INVALID_NAME),
SpinLockRelease(ShmemLock); errmsg("tranche name cannot be NULL")));
return result; if (strlen(name) >= NAMEDATALEN)
} ereport(ERROR,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("tranche name too long"),
errdetail("LWLock tranche names must be no longer than %d bytes.",
NAMEDATALEN - 1)));
/* /*
* Register a dynamic tranche name in the lookup table of the current process. * We use the ShmemLock spinlock to protect LWLockCounter and
* * LWLockTrancheNames.
* This routine will save a pointer to the tranche name passed as an argument,
* so the name should be allocated in a backend-lifetime context
* (shared memory, TopMemoryContext, static constant, or similar).
*
* The tranche name will be user-visible as a wait event name, so try to
* use a name that fits the style for those.
*/ */
void SpinLockAcquire(ShmemLock);
LWLockRegisterTranche(int tranche_id, const char *tranche_name)
{
/* This should only be called for user-defined tranches. */
if (tranche_id < LWTRANCHE_FIRST_USER_DEFINED)
return;
/* Convert to array index. */
tranche_id -= LWTRANCHE_FIRST_USER_DEFINED;
/* If necessary, create or enlarge array. */ if (*LWLockCounter - LWTRANCHE_FIRST_USER_DEFINED >= MAX_NAMED_TRANCHES)
if (tranche_id >= LWLockTrancheNamesAllocated)
{ {
int newalloc; SpinLockRelease(ShmemLock);
ereport(ERROR,
(errmsg("maximum number of tranches already registered"),
errdetail("No more than %d tranches may be registered.",
MAX_NAMED_TRANCHES)));
}
newalloc = pg_nextpower2_32(Max(8, tranche_id + 1)); result = (*LWLockCounter)++;
LocalLWLockCounter = *LWLockCounter;
strlcpy(LWLockTrancheNames[result - LWTRANCHE_FIRST_USER_DEFINED], name, NAMEDATALEN);
if (LWLockTrancheNames == NULL) SpinLockRelease(ShmemLock);
LWLockTrancheNames = (const char **)
MemoryContextAllocZero(TopMemoryContext,
newalloc * sizeof(char *));
else
LWLockTrancheNames =
repalloc0_array(LWLockTrancheNames, const char *, LWLockTrancheNamesAllocated, newalloc);
LWLockTrancheNamesAllocated = newalloc;
}
LWLockTrancheNames[tranche_id] = tranche_name; return result;
} }
/* /*
@ -637,27 +614,33 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
if (!process_shmem_requests_in_progress) if (!process_shmem_requests_in_progress)
elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook"); elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook");
if (!tranche_name)
ereport(ERROR,
(errcode(ERRCODE_INVALID_NAME),
errmsg("tranche name cannot be NULL")));
if (strlen(tranche_name) >= NAMEDATALEN)
ereport(ERROR,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("tranche name too long"),
errdetail("LWLock tranche names must be no longer than %d bytes.",
NAMEDATALEN - 1)));
if (NamedLWLockTrancheRequestArray == NULL) if (NamedLWLockTrancheRequestArray == NULL)
{ {
NamedLWLockTrancheRequestsAllocated = 16;
NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *)
MemoryContextAlloc(TopMemoryContext, MemoryContextAlloc(TopMemoryContext,
NamedLWLockTrancheRequestsAllocated MAX_NAMED_TRANCHES
* sizeof(NamedLWLockTrancheRequest)); * sizeof(NamedLWLockTrancheRequest));
} }
if (NamedLWLockTrancheRequests >= NamedLWLockTrancheRequestsAllocated) if (NamedLWLockTrancheRequests >= MAX_NAMED_TRANCHES)
{ ereport(ERROR,
int i = pg_nextpower2_32(NamedLWLockTrancheRequests + 1); (errmsg("maximum number of tranches already registered"),
errdetail("No more than %d tranches may be registered.",
NamedLWLockTrancheRequestArray = (NamedLWLockTrancheRequest *) MAX_NAMED_TRANCHES)));
repalloc(NamedLWLockTrancheRequestArray,
i * sizeof(NamedLWLockTrancheRequest));
NamedLWLockTrancheRequestsAllocated = i;
}
request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests]; request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests];
Assert(strlen(tranche_name) + 1 <= NAMEDATALEN);
strlcpy(request->tranche_name, tranche_name, NAMEDATALEN); strlcpy(request->tranche_name, tranche_name, NAMEDATALEN);
request->num_lwlocks = num_lwlocks; request->num_lwlocks = num_lwlocks;
NamedLWLockTrancheRequests++; NamedLWLockTrancheRequests++;
@ -669,6 +652,9 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
void void
LWLockInitialize(LWLock *lock, int tranche_id) LWLockInitialize(LWLock *lock, int tranche_id)
{ {
/* verify the tranche_id is valid */
(void) GetLWTrancheName(tranche_id);
pg_atomic_init_u32(&lock->state, LW_FLAG_RELEASE_OK); pg_atomic_init_u32(&lock->state, LW_FLAG_RELEASE_OK);
#ifdef LOCK_DEBUG #ifdef LOCK_DEBUG
pg_atomic_init_u32(&lock->nwaiters, 0); pg_atomic_init_u32(&lock->nwaiters, 0);
@ -710,15 +696,27 @@ GetLWTrancheName(uint16 trancheId)
return BuiltinTrancheNames[trancheId]; return BuiltinTrancheNames[trancheId];
/* /*
* It's an extension tranche, so look in LWLockTrancheNames[]. However, * We only ever add new entries to LWLockTrancheNames, so most lookups can
* it's possible that the tranche has never been registered in the current * avoid taking the spinlock as long as the backend-local counter
* process, in which case give up and return "extension". * (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.
*/ */
trancheId -= LWTRANCHE_FIRST_USER_DEFINED; if (trancheId >= LocalLWLockCounter)
{
SpinLockAcquire(ShmemLock);
LocalLWLockCounter = *LWLockCounter;
SpinLockRelease(ShmemLock);
if (trancheId >= LocalLWLockCounter)
elog(ERROR, "tranche %d is not registered", trancheId);
}
if (trancheId >= LWLockTrancheNamesAllocated || /*
LWLockTrancheNames[trancheId] == NULL) * It's an extension tranche, so look in LWLockTrancheNames.
return "extension"; */
trancheId -= LWTRANCHE_FIRST_USER_DEFINED;
return LWLockTrancheNames[trancheId]; return LWLockTrancheNames[trancheId];
} }

@ -73,14 +73,7 @@ typedef union LWLockPadded
extern PGDLLIMPORT LWLockPadded *MainLWLockArray; extern PGDLLIMPORT LWLockPadded *MainLWLockArray;
/* struct for storing named tranche information */ extern PGDLLIMPORT char **LWLockTrancheNames;
typedef struct NamedLWLockTranche
{
int trancheId;
char *trancheName;
} NamedLWLockTranche;
extern PGDLLIMPORT NamedLWLockTranche *NamedLWLockTrancheArray;
extern PGDLLIMPORT int NamedLWLockTrancheRequests; extern PGDLLIMPORT int NamedLWLockTrancheRequests;
extern PGDLLIMPORT int *LWLockCounter; extern PGDLLIMPORT int *LWLockCounter;
@ -158,19 +151,11 @@ extern LWLockPadded *GetNamedLWLockTranche(const char *tranche_name);
/* /*
* There is another, more flexible method of obtaining lwlocks. First, call * There is another, more flexible method of obtaining lwlocks. First, call
* LWLockNewTrancheId just once to obtain a tranche ID; this allocates from * LWLockNewTrancheId to obtain a tranche ID; this allocates from a shared
* a shared counter. Next, each individual process using the tranche should * counter. Second, LWLockInitialize should be called just once per lwlock,
* call LWLockRegisterTranche() to associate that tranche ID with a name. * passing the tranche ID as an argument.
* Finally, LWLockInitialize should be called just once per lwlock, passing
* the tranche ID as an argument.
*
* It may seem strange that each process using the tranche must register it
* separately, but dynamic shared memory segments aren't guaranteed to be
* mapped at the same address in all coordinating backends, so storing the
* registration in the main shared memory segment wouldn't work for that case.
*/ */
extern int LWLockNewTrancheId(void); extern int LWLockNewTrancheId(const char *name);
extern void LWLockRegisterTranche(int tranche_id, const char *tranche_name);
extern void LWLockInitialize(LWLock *lock, int tranche_id); extern void LWLockInitialize(LWLock *lock, int tranche_id);
/* /*

@ -29,8 +29,7 @@ test_dsa_basic(PG_FUNCTION_ARGS)
dsa_pointer p[100]; dsa_pointer p[100];
/* XXX: this tranche is leaked */ /* XXX: this tranche is leaked */
tranche_id = LWLockNewTrancheId(); tranche_id = LWLockNewTrancheId("test_dsa");
LWLockRegisterTranche(tranche_id, "test_dsa");
a = dsa_create(tranche_id); a = dsa_create(tranche_id);
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
@ -70,8 +69,7 @@ test_dsa_resowners(PG_FUNCTION_ARGS)
ResourceOwner childowner; ResourceOwner childowner;
/* XXX: this tranche is leaked */ /* XXX: this tranche is leaked */
tranche_id = LWLockNewTrancheId(); tranche_id = LWLockNewTrancheId("test_dsa");
LWLockRegisterTranche(tranche_id, "test_dsa");
/* Create DSA in parent resource owner */ /* Create DSA in parent resource owner */
a = dsa_create(tranche_id); a = dsa_create(tranche_id);

@ -48,7 +48,7 @@ init_tdr_dsm(void *ptr)
{ {
TestDSMRegistryStruct *dsm = (TestDSMRegistryStruct *) ptr; TestDSMRegistryStruct *dsm = (TestDSMRegistryStruct *) ptr;
LWLockInitialize(&dsm->lck, LWLockNewTrancheId()); LWLockInitialize(&dsm->lck, LWLockNewTrancheId("test_dsm_registry"));
dsm->val = 0; dsm->val = 0;
} }
@ -61,7 +61,6 @@ tdr_attach_shmem(void)
sizeof(TestDSMRegistryStruct), sizeof(TestDSMRegistryStruct),
init_tdr_dsm, init_tdr_dsm,
&found); &found);
LWLockRegisterTranche(tdr_dsm->lck.tranche, "test_dsm_registry");
if (tdr_dsa == NULL) if (tdr_dsa == NULL)
tdr_dsa = GetNamedDSA("test_dsm_registry_dsa", &found); tdr_dsa = GetNamedDSA("test_dsm_registry_dsa", &found);

@ -124,10 +124,9 @@ test_empty(void)
rt_iter *iter; rt_iter *iter;
uint64 key; uint64 key;
#ifdef TEST_SHARED_RT #ifdef TEST_SHARED_RT
int tranche_id = LWLockNewTrancheId(); int tranche_id = LWLockNewTrancheId("test_radix_tree");
dsa_area *dsa; dsa_area *dsa;
LWLockRegisterTranche(tranche_id, "test_radix_tree");
dsa = dsa_create(tranche_id); dsa = dsa_create(tranche_id);
radixtree = rt_create(dsa, tranche_id); radixtree = rt_create(dsa, tranche_id);
#else #else
@ -167,10 +166,9 @@ test_basic(rt_node_class_test_elem *test_info, int shift, bool asc)
uint64 *keys; uint64 *keys;
int children = test_info->nkeys; int children = test_info->nkeys;
#ifdef TEST_SHARED_RT #ifdef TEST_SHARED_RT
int tranche_id = LWLockNewTrancheId(); int tranche_id = LWLockNewTrancheId("test_radix_tree");
dsa_area *dsa; dsa_area *dsa;
LWLockRegisterTranche(tranche_id, "test_radix_tree");
dsa = dsa_create(tranche_id); dsa = dsa_create(tranche_id);
radixtree = rt_create(dsa, tranche_id); radixtree = rt_create(dsa, tranche_id);
#else #else
@ -304,10 +302,9 @@ test_random(void)
int num_keys = 100000; int num_keys = 100000;
uint64 *keys; uint64 *keys;
#ifdef TEST_SHARED_RT #ifdef TEST_SHARED_RT
int tranche_id = LWLockNewTrancheId(); int tranche_id = LWLockNewTrancheId("test_radix_tree");
dsa_area *dsa; dsa_area *dsa;
LWLockRegisterTranche(tranche_id, "test_radix_tree");
dsa = dsa_create(tranche_id); dsa = dsa_create(tranche_id);
radixtree = rt_create(dsa, tranche_id); radixtree = rt_create(dsa, tranche_id);
#else #else

@ -232,11 +232,9 @@ test_slru_shmem_startup(void)
(void) MakePGDirectory(slru_dir_name); (void) MakePGDirectory(slru_dir_name);
/* initialize the SLRU facility */ /* initialize the SLRU facility */
test_tranche_id = LWLockNewTrancheId(); test_tranche_id = LWLockNewTrancheId("test_slru_tranche");
LWLockRegisterTranche(test_tranche_id, "test_slru_tranche");
test_buffer_tranche_id = LWLockNewTrancheId(); test_buffer_tranche_id = LWLockNewTrancheId("test_buffer_tranche");
LWLockRegisterTranche(test_tranche_id, "test_buffer_tranche");
TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically; TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
SimpleLruInit(TestSlruCtl, "TestSLRU", SimpleLruInit(TestSlruCtl, "TestSLRU",

@ -103,8 +103,7 @@ test_create(PG_FUNCTION_ARGS)
{ {
int tranche_id; int tranche_id;
tranche_id = LWLockNewTrancheId(); tranche_id = LWLockNewTrancheId("test_tidstore");
LWLockRegisterTranche(tranche_id, "test_tidstore");
tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id); tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id);

Loading…
Cancel
Save