mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Introduce local hash table for lock state, as per recent proposal.
PROCLOCK structs in shared memory now have only a bitmask for held locks, rather than counts (making them 40 bytes smaller, which is a good thing). Multiple locks within a transaction are counted in the local hash table instead, and we have provision for tracking which ResourceOwner each count belongs to. Solves recently reported problem with memory leakage within long transactions.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.14 2003/11/29 19:51:56 pgsql Exp $
|
||||
$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.15 2004/08/27 17:07:41 tgl Exp $
|
||||
|
||||
|
||||
LOCKING OVERVIEW
|
||||
@@ -47,12 +47,6 @@ The rest of this README file discusses the regular lock manager in detail.
|
||||
|
||||
LOCK DATA STRUCTURES
|
||||
|
||||
There are two fundamental lock structures: the per-lockable-object LOCK
|
||||
struct, and the per-lock PROCLOCK struct. A LOCK object exists
|
||||
for each lockable object that currently has locks held or requested on it.
|
||||
A PROCLOCK struct exists for each transaction that is holding or requesting
|
||||
lock(s) on each LOCK object.
|
||||
|
||||
Lock methods describe the overall locking behavior. Currently there are
|
||||
two lock methods: DEFAULT and USER. (USER locks are non-blocking.)
|
||||
|
||||
@@ -60,6 +54,20 @@ Lock modes describe the type of the lock (read/write or shared/exclusive).
|
||||
See src/tools/backend/index.html and src/include/storage/lock.h for more
|
||||
details.
|
||||
|
||||
There are two fundamental lock structures in shared memory: the
|
||||
per-lockable-object LOCK struct, and the per-lock-and-requestor PROCLOCK
|
||||
struct. A LOCK object exists for each lockable object that currently has
|
||||
locks held or requested on it. A PROCLOCK struct exists for each transaction
|
||||
that is holding or requesting lock(s) on each LOCK object.
|
||||
|
||||
In addition to these, each backend maintains an unshared LOCALLOCK structure
|
||||
for each lockable object and lock mode that it is currently holding or
|
||||
requesting. The shared lock structures only allow a single lock grant to
|
||||
be made per lockable object/lock mode/transaction. Internally to a backend,
|
||||
however, the same lock may be requested and perhaps released multiple times
|
||||
in a transaction. The internal request counts are held in LOCALLOCK so that
|
||||
the shared LockMgrLock need not be obtained to alter them.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
The lock manager's LOCK objects contain:
|
||||
@@ -103,7 +111,7 @@ waitMask -
|
||||
This bitmask shows the types of locks being waited for. Bit i of waitMask
|
||||
is 1 if and only if requested[i] > granted[i].
|
||||
|
||||
lockHolders -
|
||||
procLocks -
|
||||
This is a shared memory queue of all the PROCLOCK structs associated with
|
||||
the lock object. Note that both granted and waiting PROCLOCKs are in this
|
||||
list (indeed, the same PROCLOCK might have some already-granted locks and
|
||||
@@ -120,7 +128,10 @@ nRequested -
|
||||
acquired. The count includes attempts by processes which were put
|
||||
to sleep due to conflicts. It also counts the same backend twice
|
||||
if, for example, a backend process first acquires a read and then
|
||||
acquires a write, or acquires a read lock twice.
|
||||
acquires a write, or acquires the lock under two different transaction
|
||||
IDs. (But multiple acquisitions of the same lock/lock mode under the
|
||||
same transaction ID are not multiply counted here; they are recorded
|
||||
only in the backend's LOCALLOCK structure.)
|
||||
|
||||
requested -
|
||||
Keeps a count of how many locks of each type have been attempted. Only
|
||||
@@ -130,9 +141,8 @@ requested -
|
||||
|
||||
nGranted -
|
||||
Keeps count of how many times this lock has been successfully acquired.
|
||||
This count does not include attempts that are waiting due to conflicts,
|
||||
but can count the same backend twice (e.g. a read then a write -- since
|
||||
its the same transaction this won't cause a conflict).
|
||||
This count does not include attempts that are waiting due to conflicts.
|
||||
Otherwise the counting rules are the same as for nRequested.
|
||||
|
||||
granted -
|
||||
Keeps count of how many locks of each type are currently held. Once again
|
||||
@@ -164,18 +174,17 @@ tag -
|
||||
if the PROCLOCK is for session-level locking.
|
||||
|
||||
Note that this structure will support multiple transactions running
|
||||
concurrently in one backend, which may be handy if we someday decide
|
||||
to support nested transactions. Currently, the XID field is only needed
|
||||
to distinguish per-transaction locks from session locks. User locks
|
||||
are always session locks, and we also use session locks for multi-
|
||||
transaction operations like VACUUM.
|
||||
concurrently in one backend. Currently we do not use it for that
|
||||
purpose: subtransactions acquire locks in the name of their top parent
|
||||
transaction, to simplify reassigning lock ownership at subtransaction end.
|
||||
So the XID field is really only needed to distinguish per-transaction
|
||||
locks from session locks. User locks are always session locks, and we
|
||||
also use session locks for multi-transaction operations like VACUUM.
|
||||
|
||||
holding -
|
||||
The number of successfully acquired locks of each type for this PROCLOCK.
|
||||
This should be <= the corresponding granted[] value of the lock object!
|
||||
|
||||
nHolding -
|
||||
Sum of the holding[] array.
|
||||
holdMask -
|
||||
A bitmask for the lock types successfully acquired by this PROCLOCK.
|
||||
This should be a subset of the LOCK object's grantMask, and also a
|
||||
subset of the PGPROC object's heldLocks mask.
|
||||
|
||||
lockLink -
|
||||
List link for shared memory queue of all the PROCLOCK objects for the
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.29 2004/07/21 22:31:22 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.30 2004/08/27 17:07:41 tgl Exp $
|
||||
*
|
||||
* Interface:
|
||||
*
|
||||
@@ -427,7 +427,7 @@ FindLockCycleRecurse(PGPROC *checkProc,
|
||||
PGPROC *proc;
|
||||
LOCK *lock;
|
||||
PROCLOCK *proclock;
|
||||
SHM_QUEUE *lockHolders;
|
||||
SHM_QUEUE *procLocks;
|
||||
LockMethod lockMethodTable;
|
||||
PROC_QUEUE *waitQueue;
|
||||
int queue_size;
|
||||
@@ -483,9 +483,9 @@ FindLockCycleRecurse(PGPROC *checkProc,
|
||||
* Scan for procs that already hold conflicting locks. These are
|
||||
* "hard" edges in the waits-for graph.
|
||||
*/
|
||||
lockHolders = &(lock->lockHolders);
|
||||
procLocks = &(lock->procLocks);
|
||||
|
||||
proclock = (PROCLOCK *) SHMQueueNext(lockHolders, lockHolders,
|
||||
proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks,
|
||||
offsetof(PROCLOCK, lockLink));
|
||||
|
||||
while (proclock)
|
||||
@@ -497,8 +497,8 @@ FindLockCycleRecurse(PGPROC *checkProc,
|
||||
{
|
||||
for (lm = 1; lm <= numLockModes; lm++)
|
||||
{
|
||||
if (proclock->holding[lm] > 0 &&
|
||||
((1 << lm) & conflictMask) != 0)
|
||||
if ((proclock->holdMask & LOCKBIT_ON(lm)) &&
|
||||
(conflictMask & LOCKBIT_ON(lm)))
|
||||
{
|
||||
/* This proc hard-blocks checkProc */
|
||||
if (FindLockCycleRecurse(proc, depth + 1,
|
||||
@@ -519,7 +519,7 @@ FindLockCycleRecurse(PGPROC *checkProc,
|
||||
}
|
||||
}
|
||||
|
||||
proclock = (PROCLOCK *) SHMQueueNext(lockHolders, &proclock->lockLink,
|
||||
proclock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->lockLink,
|
||||
offsetof(PROCLOCK, lockLink));
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.150 2004/07/17 03:28:51 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.151 2004/08/27 17:07:41 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -46,11 +46,11 @@
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "access/xact.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/sinval.h"
|
||||
#include "storage/spin.h"
|
||||
#include "utils/resowner.h"
|
||||
|
||||
|
||||
/* GUC variables */
|
||||
@@ -76,11 +76,6 @@ static PGPROC *DummyProcs = NULL;
|
||||
static bool waitingForLock = false;
|
||||
static bool waitingForSignal = false;
|
||||
|
||||
/* Auxiliary state, valid when waitingForLock is true */
|
||||
static LOCKTAG waitingForLockTag;
|
||||
static TransactionId waitingForLockXid;
|
||||
static LOCKMODE waitingForLockMode;
|
||||
|
||||
/* Mark these volatile because they can be changed by signal handler */
|
||||
static volatile bool statement_timeout_active = false;
|
||||
static volatile bool deadlock_timeout_active = false;
|
||||
@@ -250,8 +245,8 @@ InitProcess(void)
|
||||
MyProc->lwExclusive = false;
|
||||
MyProc->lwWaitLink = NULL;
|
||||
MyProc->waitLock = NULL;
|
||||
MyProc->waitHolder = NULL;
|
||||
SHMQueueInit(&(MyProc->procHolders));
|
||||
MyProc->waitProcLock = NULL;
|
||||
SHMQueueInit(&(MyProc->procLocks));
|
||||
|
||||
/*
|
||||
* Arrange to clean up at backend exit.
|
||||
@@ -323,8 +318,8 @@ InitDummyProcess(int proctype)
|
||||
MyProc->lwExclusive = false;
|
||||
MyProc->lwWaitLink = NULL;
|
||||
MyProc->waitLock = NULL;
|
||||
MyProc->waitHolder = NULL;
|
||||
SHMQueueInit(&(MyProc->procHolders));
|
||||
MyProc->waitProcLock = NULL;
|
||||
SHMQueueInit(&(MyProc->procLocks));
|
||||
|
||||
/*
|
||||
* Arrange to clean up at process exit.
|
||||
@@ -372,18 +367,10 @@ LockWaitCancel(void)
|
||||
* Somebody kicked us off the lock queue already. Perhaps they
|
||||
* granted us the lock, or perhaps they detected a deadlock.
|
||||
* If they did grant us the lock, we'd better remember it in
|
||||
* CurrentResourceOwner.
|
||||
*
|
||||
* Exception: if CurrentResourceOwner is NULL then we can't do
|
||||
* anything. This could only happen when we are invoked from ProcKill
|
||||
* or some similar place, where all our locks are about to be released
|
||||
* anyway.
|
||||
* our local lock table.
|
||||
*/
|
||||
if (MyProc->waitStatus == STATUS_OK && CurrentResourceOwner != NULL)
|
||||
ResourceOwnerRememberLock(CurrentResourceOwner,
|
||||
&waitingForLockTag,
|
||||
waitingForLockXid,
|
||||
waitingForLockMode);
|
||||
if (MyProc->waitStatus == STATUS_OK)
|
||||
GrantAwaitedLock();
|
||||
}
|
||||
|
||||
waitingForLock = false;
|
||||
@@ -433,7 +420,7 @@ ProcReleaseLocks(bool isCommit)
|
||||
/* If waiting, get off wait queue (should only be needed after error) */
|
||||
LockWaitCancel();
|
||||
/* Release locks */
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, !isCommit);
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, !isCommit);
|
||||
}
|
||||
|
||||
|
||||
@@ -466,11 +453,11 @@ ProcKill(int code, Datum arg)
|
||||
LockWaitCancel();
|
||||
|
||||
/* Remove from the standard lock table */
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, true);
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, true);
|
||||
|
||||
#ifdef USER_LOCKS
|
||||
/* Remove from the user lock table */
|
||||
LockReleaseAll(USER_LOCKMETHOD, MyProc, true);
|
||||
LockReleaseAll(USER_LOCKMETHOD, true);
|
||||
#endif
|
||||
|
||||
SpinLockAcquire(ProcStructLock);
|
||||
@@ -644,10 +631,7 @@ ProcSleep(LockMethod lockMethodTable,
|
||||
{
|
||||
/* Skip the wait and just grant myself the lock. */
|
||||
GrantLock(lock, proclock, lockmode);
|
||||
ResourceOwnerRememberLock(CurrentResourceOwner,
|
||||
&lock->tag,
|
||||
proclock->tag.xid,
|
||||
lockmode);
|
||||
GrantAwaitedLock();
|
||||
return STATUS_OK;
|
||||
}
|
||||
/* Break out of loop to put myself before him */
|
||||
@@ -680,7 +664,7 @@ ProcSleep(LockMethod lockMethodTable,
|
||||
|
||||
/* Set up wait information in PGPROC object, too */
|
||||
MyProc->waitLock = lock;
|
||||
MyProc->waitHolder = proclock;
|
||||
MyProc->waitProcLock = proclock;
|
||||
MyProc->waitLockMode = lockmode;
|
||||
|
||||
MyProc->waitStatus = STATUS_ERROR; /* initialize result for error */
|
||||
@@ -697,9 +681,6 @@ ProcSleep(LockMethod lockMethodTable,
|
||||
}
|
||||
|
||||
/* mark that we are waiting for a lock */
|
||||
waitingForLockTag = lock->tag;
|
||||
waitingForLockXid = proclock->tag.xid;
|
||||
waitingForLockMode = lockmode;
|
||||
waitingForLock = true;
|
||||
|
||||
/*
|
||||
@@ -737,8 +718,8 @@ ProcSleep(LockMethod lockMethodTable,
|
||||
* promise that we don't mind losing control to a cancel/die interrupt
|
||||
* here. We don't, because we have no shared-state-change work to do
|
||||
* after being granted the lock (the grantor did it all). We do have
|
||||
* to worry about updating the local CurrentResourceOwner, but if we
|
||||
* lose control to an error, LockWaitCancel will fix that up.
|
||||
* to worry about updating the locallock table, but if we lose control
|
||||
* to an error, LockWaitCancel will fix that up.
|
||||
*/
|
||||
PGSemaphoreLock(&MyProc->sem, true);
|
||||
|
||||
@@ -751,8 +732,7 @@ ProcSleep(LockMethod lockMethodTable,
|
||||
/*
|
||||
* Re-acquire the locktable's masterLock. We have to do this to hold
|
||||
* off cancel/die interrupts before we can mess with waitingForLock
|
||||
* (else we might have a missed or duplicated CurrentResourceOwner
|
||||
* update).
|
||||
* (else we might have a missed or duplicated locallock update).
|
||||
*/
|
||||
LWLockAcquire(masterLock, LW_EXCLUSIVE);
|
||||
|
||||
@@ -762,13 +742,10 @@ ProcSleep(LockMethod lockMethodTable,
|
||||
waitingForLock = false;
|
||||
|
||||
/*
|
||||
* If we got the lock, be sure to remember it in CurrentResourceOwner.
|
||||
* If we got the lock, be sure to remember it in the locallock table.
|
||||
*/
|
||||
if (MyProc->waitStatus == STATUS_OK)
|
||||
ResourceOwnerRememberLock(CurrentResourceOwner,
|
||||
&lock->tag,
|
||||
proclock->tag.xid,
|
||||
lockmode);
|
||||
GrantAwaitedLock();
|
||||
|
||||
/*
|
||||
* We don't have to do anything else, because the awaker did all the
|
||||
@@ -809,7 +786,7 @@ ProcWakeup(PGPROC *proc, int waitStatus)
|
||||
|
||||
/* Clean up process' state and pass it the ok/fail signal */
|
||||
proc->waitLock = NULL;
|
||||
proc->waitHolder = NULL;
|
||||
proc->waitProcLock = NULL;
|
||||
proc->waitStatus = waitStatus;
|
||||
|
||||
/* And awaken it */
|
||||
@@ -850,12 +827,12 @@ ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock)
|
||||
LockCheckConflicts(lockMethodTable,
|
||||
lockmode,
|
||||
lock,
|
||||
proc->waitHolder,
|
||||
proc->waitProcLock,
|
||||
proc,
|
||||
NULL) == STATUS_OK)
|
||||
{
|
||||
/* OK to waken */
|
||||
GrantLock(lock, proc->waitHolder, lockmode);
|
||||
GrantLock(lock, proc->waitProcLock, lockmode);
|
||||
proc = ProcWakeup(proc, STATUS_OK);
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user