doc/src/sgml/func.sgml | 20 ++++++++++ src/backend/access/transam/twophase.c | 1 + src/backend/storage/lmgr/lwlock.c | 19 +++++++++ src/backend/storage/lmgr/proc.c | 2 + src/backend/utils/adt/lockfuncs.c | 74 +++++++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 8 ++++ src/include/storage/lwlock.h | 24 ++++++------ src/include/storage/proc.h | 3 ++ 8 files changed, 140 insertions(+), 11 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index b682154f63..529e722b28 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -20958,6 +20958,26 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); + + + + pg_lwlock_blocking_pid + + pg_lwlock_blocking_pid ( integer ) + integer[] + + + Returns the waiting mode of the the specified process ID, + the last process ID (and its holding mode) holding the LWLock, the number + of process holding the LWLlock, or empty values if there is no such server process + or it is not blocked. + + + One server process blocks another if it holds a lwlock that + conflicts with the blocked process's lwlock request. + + + diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index e1904877fa..d9be3b06cc 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -475,6 +475,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid, proc->isBackgroundWorker = false; proc->lwWaiting = false; proc->lwWaitMode = 0; + proc->lwLastHoldingPid = 0; proc->waitLock = NULL; proc->waitProcLock = NULL; for (i = 0; i < NUM_LOCK_PARTITIONS; i++) diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 2fa90cc095..b617c18916 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -749,6 +749,8 @@ LWLockInitialize(LWLock *lock, int tranche_id) pg_atomic_init_u32(&lock->nwaiters, 0); #endif lock->tranche = tranche_id; + lock->last_holding_pid = 0; + pg_atomic_init_u32(&lock->nholders, 0); proclist_init(&lock->waiters); } @@ -872,6 +874,11 @@ LWLockAttemptLock(LWLock *lock, LWLockMode mode) if (lock_free) { /* Great! Got the lock. */ + if (MyProc) { + lock->last_holding_pid = MyProc->pid; + lock->last_mode = mode; + pg_atomic_fetch_add_u32(&lock->nholders, 1); + } #ifdef LOCK_DEBUG if (mode == LW_EXCLUSIVE) lock->owner = MyProc; @@ -1056,6 +1063,7 @@ LWLockWakeup(LWLock *lock) */ pg_write_barrier(); waiter->lwWaiting = false; + waiter->lwLastHoldingPid = 0; PGSemaphoreUnlock(waiter->sem); } } @@ -1322,6 +1330,9 @@ LWLockAcquire(LWLock *lock, LWLockMode mode) lwstats->block_count++; #endif + MyProc->lwLastHoldingPid = lock->last_holding_pid; + MyProc->lwHolderMode = lock->last_mode; + MyProc->lwNbHolders = pg_atomic_read_u32(&lock->nholders); LWLockReportWaitStart(lock); TRACE_POSTGRESQL_LWLOCK_WAIT_START(T_NAME(lock), mode); @@ -1483,6 +1494,9 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode) lwstats->block_count++; #endif + MyProc->lwLastHoldingPid = lock->last_holding_pid; + MyProc->lwHolderMode = lock->last_mode; + MyProc->lwNbHolders = pg_atomic_read_u32(&lock->nholders); LWLockReportWaitStart(lock); TRACE_POSTGRESQL_LWLOCK_WAIT_START(T_NAME(lock), mode); @@ -1699,6 +1713,9 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval) lwstats->block_count++; #endif + MyProc->lwLastHoldingPid = lock->last_holding_pid; + MyProc->lwHolderMode = lock->last_mode; + MyProc->lwNbHolders = pg_atomic_read_u32(&lock->nholders); LWLockReportWaitStart(lock); TRACE_POSTGRESQL_LWLOCK_WAIT_START(T_NAME(lock), LW_EXCLUSIVE); @@ -1800,6 +1817,7 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val) /* check comment in LWLockWakeup() about this barrier */ pg_write_barrier(); waiter->lwWaiting = false; + waiter->lwLastHoldingPid = 0; PGSemaphoreUnlock(waiter->sem); } } @@ -1844,6 +1862,7 @@ LWLockRelease(LWLock *lock) else oldstate = pg_atomic_sub_fetch_u32(&lock->state, LW_VAL_SHARED); + pg_atomic_fetch_sub_u32(&lock->nholders, 1); /* nobody else can have that kind of lock */ Assert(!(oldstate & LW_VAL_EXCLUSIVE)); diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index f5eef6fa4e..9ec7dab9a1 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -402,6 +402,7 @@ InitProcess(void) if (IsAutoVacuumWorkerProcess()) MyPgXact->vacuumFlags |= PROC_IS_AUTOVACUUM; MyProc->lwWaiting = false; + MyProc->lwLastHoldingPid = 0; MyProc->lwWaitMode = 0; MyProc->waitLock = NULL; MyProc->waitProcLock = NULL; @@ -581,6 +582,7 @@ InitAuxiliaryProcess(void) MyProc->delayChkpt = false; MyPgXact->vacuumFlags = 0; MyProc->lwWaiting = false; + MyProc->lwLastHoldingPid = 0; MyProc->lwWaitMode = 0; MyProc->waitLock = NULL; MyProc->waitProcLock = NULL; diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index e992d1bbfc..3282988205 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -20,6 +20,8 @@ #include "storage/predicate_internals.h" #include "utils/array.h" #include "utils/builtins.h" +#include "storage/procarray.h" +#include "storage/proc.h" /* @@ -525,6 +527,78 @@ pg_blocking_pids(PG_FUNCTION_ARGS) sizeof(int32), true, TYPALIGN_INT)); } +/* + * pg_lwlock_blocking_pid + * + * returns the waiting mode of the the specified process ID, + * the last process ID (and its holding mode) holding the LWLock, the number + * of process holding the LWLlock, or empty values if there is no such server process + * or it is not blocked. + */ +Datum +pg_lwlock_blocking_pid(PG_FUNCTION_ARGS) +{ + int blocked_pid = PG_GETARG_INT32(0); + PGPROC *proc = NULL; + Datum values[4]; + bool nulls[4]; + TupleDesc tupdesc; + HeapTuple htup; + + /* + * Construct a tuple descriptor for the result row. + */ + tupdesc = CreateTemplateTupleDesc(4); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "requested_mode", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "last_holder_pid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "last_holder_mode", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "nb_holders", + INT2OID, -1, 0); + + tupdesc = BlessTupleDesc(tupdesc); + /* + * Form tuple with appropriate data. + */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, false, sizeof(nulls)); + + proc = BackendPidGetProc(blocked_pid); + + if (proc != NULL && proc->lwWaiting) { + if (proc->lwWaitMode == 0) + values[0] = CStringGetTextDatum("LW_EXCLUSIVE"); + else if (proc->lwWaitMode == 1) + values[0] = CStringGetTextDatum("LW_SHARED"); + else if (proc->lwWaitMode == 2) + values[0] = CStringGetTextDatum("LW_WAIT_UNTIL_FREE"); + else + values[0] = CStringGetTextDatum("N/A"); + + values[1] = Int32GetDatum(proc->lwLastHoldingPid); + + if (proc->lwHolderMode == 0) + values[2] = CStringGetTextDatum("LW_EXCLUSIVE"); + else if (proc->lwHolderMode == 1) + values[2] = CStringGetTextDatum("LW_SHARED"); + else if (proc->lwHolderMode == 2) + values[2] = CStringGetTextDatum("LW_WAIT_UNTIL_FREE"); + else + values[2] = CStringGetTextDatum("N/A"); + values[3] = Int16GetDatum(proc->lwNbHolders); + } else { + nulls[0] = true; + nulls[1] = true; + nulls[2] = true; + nulls[3] = true; + } + + htup = heap_form_tuple(tupdesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(htup)); +} /* * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 61f2c2f5b4..d06ec798de 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5916,6 +5916,14 @@ descr => 'get array of PIDs of sessions blocking specified backend PID from acquiring a heavyweight lock', proname => 'pg_blocking_pids', provolatile => 'v', prorettype => '_int4', proargtypes => 'int4', prosrc => 'pg_blocking_pids' }, +{ oid => '8279', + descr => 'get informations of the session(s) blocking specified backend PID from acquiring a lightweight lock', + proname => 'pg_lwlock_blocking_pid', provolatile => 'v', prorettype => 'record', + proargtypes => 'int4', + proallargtypes => '{int4,text,int4,text,int2}', + proargmodes => '{i,o,o,o,o}', + proargnames => '{pid,requested_mode,last_holder_pid,last_holder_mode,nb_holders}', + prosrc => 'pg_lwlock_blocking_pid' }, { oid => '3376', descr => 'get array of PIDs of sessions blocking specified backend PID from acquiring a safe snapshot', proname => 'pg_safe_snapshot_blocking_pids', provolatile => 'v', diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index c04ae97148..a352ca1efb 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -24,6 +24,15 @@ struct PGPROC; +typedef enum LWLockMode +{ + LW_EXCLUSIVE, + LW_SHARED, + LW_WAIT_UNTIL_FREE /* A special mode used in PGPROC->lwWaitMode, + * when waiting for lock to become free. Not + * to be used as LWLockAcquire argument */ +} LWLockMode; + /* * Code outside of lwlock.c should not manipulate the contents of this * structure directly, but we have to declare it here to allow LWLocks to be @@ -31,9 +40,12 @@ struct PGPROC; */ typedef struct LWLock { - uint16 tranche; /* tranche ID */ + uint16 tranche; /* tranche ID */ pg_atomic_uint32 state; /* state of exclusive/nonexclusive lockers */ proclist_head waiters; /* list of waiting PGPROCs */ + int last_holding_pid; /* last pid owner of the lock */ + LWLockMode last_mode; /* last holding mode of the last pid owner of the lock */ + pg_atomic_uint32 nholders; /* number of holders (could be >1 in case of LW_SHARED */ #ifdef LOCK_DEBUG pg_atomic_uint32 nwaiters; /* number of waiters */ struct PGPROC *owner; /* last exclusive owner of the lock */ @@ -128,16 +140,6 @@ extern PGDLLIMPORT int NamedLWLockTrancheRequests; #define NUM_FIXED_LWLOCKS \ (PREDICATELOCK_MANAGER_LWLOCK_OFFSET + NUM_PREDICATELOCK_PARTITIONS) -typedef enum LWLockMode -{ - LW_EXCLUSIVE, - LW_SHARED, - LW_WAIT_UNTIL_FREE /* A special mode used in PGPROC->lwWaitMode, - * when waiting for lock to become free. Not - * to be used as LWLockAcquire argument */ -} LWLockMode; - - #ifdef LOCK_DEBUG extern bool Trace_lwlocks; #endif diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 1ee9000b2b..829a8a4421 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -130,6 +130,9 @@ struct PGPROC bool lwWaiting; /* true if waiting for an LW lock */ uint8 lwWaitMode; /* lwlock mode being waited for */ proclist_node lwWaitLink; /* position in LW lock wait list */ + int lwLastHoldingPid; /* last holder of the LW lock */ + LWLockMode lwHolderMode; /* LW lock mode of last holder of the LW lock */ + uint32 lwNbHolders; /* number of holders of the LW lock */ /* Support for condition variables. */ proclist_node cvWaitLink; /* position in CV wait list */