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 */