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:
Tom Lane 2004-08-27 17:07:42 +00:00
parent ef16b4e157
commit 1785acebf2
10 changed files with 729 additions and 495 deletions

View File

@ -75,7 +75,7 @@ user_write_unlock_oid(Oid oid)
int
user_unlock_all(void)
{
return LockReleaseAll(USER_LOCKMETHOD, MyProc, true);
return LockReleaseAll(USER_LOCKMETHOD, true);
}
/* end of file */

View File

@ -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

View File

@ -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

View File

@ -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);
/*

View File

@ -6,7 +6,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.14 2004/08/27 17:07:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -92,7 +92,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
LOCK *lock;
PGPROC *proc;
bool granted;
LOCKMODE mode;
LOCKMODE mode = 0;
Datum values[6];
char nulls[6];
HeapTuple tuple;
@ -108,13 +108,16 @@ pg_lock_status(PG_FUNCTION_ARGS)
* report again.
*/
granted = false;
for (mode = 0; mode < MAX_LOCKMODES; mode++)
if (proclock->holdMask)
{
if (proclock->holding[mode] > 0)
for (mode = 0; mode < MAX_LOCKMODES; mode++)
{
granted = true;
proclock->holding[mode] = 0;
break;
if (proclock->holdMask & LOCKBIT_ON(mode))
{
granted = true;
proclock->holdMask &= LOCKBIT_OFF(mode);
break;
}
}
}

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.3 2004/08/25 18:43:43 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.4 2004/08/27 17:07:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,18 +30,6 @@
#include "utils/relcache.h"
/*
* Info needed to identify/release a lock
*/
typedef struct LockIdData
{
/* we assume lockmethodid is part of locktag */
LOCKTAG locktag;
TransactionId xid;
LOCKMODE lockmode;
} LockIdData;
/*
* ResourceOwner objects look like this
*/
@ -57,11 +45,6 @@ typedef struct ResourceOwnerData
Buffer *buffers; /* dynamically allocated array */
int maxbuffers; /* currently allocated array size */
/* We have built-in support for remembering owned locks */
int nlocks; /* number of owned locks */
LockIdData *locks; /* dynamically allocated array */
int maxlocks; /* currently allocated array size */
/* We have built-in support for remembering catcache references */
int ncatrefs; /* number of owned catcache pins */
HeapTuple *catrefs; /* dynamically allocated array */
@ -274,44 +257,19 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
*/
if (owner == TopTransactionResourceOwner)
ProcReleaseLocks(isCommit);
/* Mark object as holding no locks, just for sanity */
owner->nlocks = 0;
}
else
{
/*
* Release locks retail. Note that LockRelease will remove
* the lock entry from my list, so I just have to iterate till
* there are none. Also note that if we are committing a
* Release locks retail. Note that if we are committing a
* subtransaction, we do NOT release its locks yet, but transfer
* them to the parent.
*
* XXX as above, this is a bit inefficient but probably not worth
* the trouble to optimize more.
*/
Assert(owner->parent != NULL);
while (owner->nlocks > 0)
{
LockIdData *lockid = &owner->locks[owner->nlocks - 1];
if (isCommit)
{
ResourceOwnerEnlargeLocks(owner->parent);
ResourceOwnerRememberLock(owner->parent,
&lockid->locktag,
lockid->xid,
lockid->lockmode);
owner->nlocks--;
}
else
{
LockRelease(lockid->locktag.lockmethodid,
&lockid->locktag,
lockid->xid,
lockid->lockmode);
/* LockRelease will have removed the entry from list */
}
}
if (isCommit)
LockReassignCurrentOwner();
else
LockReleaseCurrentOwner();
}
}
else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
@ -368,7 +326,6 @@ ResourceOwnerDelete(ResourceOwner owner)
/* And it better not own any resources, either */
Assert(owner->nbuffers == 0);
Assert(owner->nlocks == 0);
Assert(owner->ncatrefs == 0);
Assert(owner->ncatlistrefs == 0);
Assert(owner->nrelrefs == 0);
@ -390,8 +347,6 @@ ResourceOwnerDelete(ResourceOwner owner)
/* And free the object. */
if (owner->buffers)
pfree(owner->buffers);
if (owner->locks)
pfree(owner->locks);
if (owner->catrefs)
pfree(owner->catrefs);
if (owner->catlistrefs)
@ -402,6 +357,15 @@ ResourceOwnerDelete(ResourceOwner owner)
pfree(owner);
}
/*
* Fetch parent of a ResourceOwner (returns NULL if top-level owner)
*/
ResourceOwner
ResourceOwnerGetParent(ResourceOwner owner)
{
return owner->parent;
}
/*
* Reassign a ResourceOwner to have a new parent
*/
@ -581,97 +545,6 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
}
}
/*
* Make sure there is room for at least one more entry in a ResourceOwner's
* lock array.
*
* This is separate from actually inserting an entry because if we run out
* of memory, it's critical to do so *before* acquiring the resource.
*/
void
ResourceOwnerEnlargeLocks(ResourceOwner owner)
{
int newmax;
if (owner->nlocks < owner->maxlocks)
return; /* nothing to do */
if (owner->locks == NULL)
{
newmax = 16;
owner->locks = (LockIdData *)
MemoryContextAlloc(TopMemoryContext, newmax * sizeof(LockIdData));
owner->maxlocks = newmax;
}
else
{
newmax = owner->maxlocks * 2;
owner->locks = (LockIdData *)
repalloc(owner->locks, newmax * sizeof(LockIdData));
owner->maxlocks = newmax;
}
}
/*
* Remember that a lock is owned by a ResourceOwner
*
* Caller must have previously done ResourceOwnerEnlargeLocks()
*/
void
ResourceOwnerRememberLock(ResourceOwner owner,
LOCKTAG *locktag,
TransactionId xid,
LOCKMODE lockmode)
{
/* Session locks and user locks are not transactional */
if (xid != InvalidTransactionId &&
locktag->lockmethodid == DEFAULT_LOCKMETHOD)
{
Assert(owner->nlocks < owner->maxlocks);
owner->locks[owner->nlocks].locktag = *locktag;
owner->locks[owner->nlocks].xid = xid;
owner->locks[owner->nlocks].lockmode = lockmode;
owner->nlocks++;
}
}
/*
* Forget that a lock is owned by a ResourceOwner
*/
void
ResourceOwnerForgetLock(ResourceOwner owner,
LOCKTAG *locktag,
TransactionId xid,
LOCKMODE lockmode)
{
/* Session locks and user locks are not transactional */
if (xid != InvalidTransactionId &&
locktag->lockmethodid == DEFAULT_LOCKMETHOD)
{
LockIdData *locks = owner->locks;
int nl1 = owner->nlocks - 1;
int i;
for (i = nl1; i >= 0; i--)
{
if (memcmp(&locks[i].locktag, locktag, sizeof(LOCKTAG)) == 0 &&
locks[i].xid == xid &&
locks[i].lockmode == lockmode)
{
while (i < nl1)
{
locks[i] = locks[i + 1];
i++;
}
owner->nlocks = nl1;
return;
}
}
elog(ERROR, "lock %u/%u/%u is not owned by resource owner %s",
locktag->relId, locktag->dbId, locktag->objId.xid, owner->name);
}
}
/*
* Make sure there is room for at least one more entry in a ResourceOwner's
* catcache reference array.

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.80 2004/08/26 17:22:28 tgl Exp $
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.81 2004/08/27 17:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -69,15 +69,17 @@ typedef uint16 LOCKMETHODID;
#define LockMethodIsValid(lockmethodid) ((lockmethodid) != INVALID_LOCKMETHOD)
extern int NumLockMethods;
/*
* This is the control structure for a lock table. It lives in shared
* memory. Currently, none of these fields change after startup. In addition
* to the LockMethodData, a lock table has a "lockHash" table holding
* per-locked-object lock information, and a "proclockHash" table holding
* per-lock-holder/waiter lock information.
* to the LockMethodData, a lock table has a shared "lockHash" table holding
* per-locked-object lock information, and a shared "proclockHash" table
* holding per-lock-holder/waiter lock information.
*
* lockmethodid -- the handle used by the lock table's clients to
* refer to the type of lock table being used.
* masterLock -- LWLock used to synchronize access to the table
*
* numLockModes -- number of lock types (READ,WRITE,etc) that
* are defined on this lock table
@ -85,16 +87,14 @@ typedef uint16 LOCKMETHODID;
* conflictTab -- this is an array of bitmasks showing lock
* type conflicts. conflictTab[i] is a mask with the j-th bit
* turned on if lock types i and j conflict.
*
* masterLock -- LWLock used to synchronize access to the table
*/
typedef struct LockMethodData
{
LOCKMETHODID lockmethodid;
LWLockId masterLock;
int numLockModes;
LOCKMASK conflictTab[MAX_LOCKMODES];
LWLockId masterLock;
} LockMethodData;
typedef LockMethodData *LockMethod;
@ -113,8 +113,8 @@ typedef struct LOCKTAG
} objId;
/*
* offnum should be part of objId.tupleId above, but would increase
* sizeof(LOCKTAG) and so moved here; currently used by userlocks
* offnum should be part of objId union above, but doing that would
* increase sizeof(LOCKTAG) due to padding. Currently used by userlocks
* only.
*/
OffsetNumber offnum;
@ -129,7 +129,7 @@ typedef struct LOCKTAG
* tag -- uniquely identifies the object being locked
* grantMask -- bitmask for all lock types currently granted on this object.
* waitMask -- bitmask for all lock types currently awaited on this object.
* lockHolders -- list of PROCLOCK objects for this lock.
* procLocks -- list of PROCLOCK objects for this lock.
* waitProcs -- queue of processes waiting for this lock.
* requested -- count of each lock type currently requested on the lock
* (includes requests already granted!!).
@ -145,7 +145,7 @@ typedef struct LOCK
/* data */
LOCKMASK grantMask; /* bitmask for lock types already granted */
LOCKMASK waitMask; /* bitmask for lock types awaited */
SHM_QUEUE lockHolders; /* list of PROCLOCK objects assoc. with
SHM_QUEUE procLocks; /* list of PROCLOCK objects assoc. with
* lock */
PROC_QUEUE waitProcs; /* list of PGPROC objects waiting on lock */
int requested[MAX_LOCKMODES]; /* counts of requested
@ -168,7 +168,7 @@ typedef struct LOCK
* proclock hashtable. A PROCLOCKTAG value uniquely identifies the combination
* of a lockable object and a holder/waiter for that object.
*
* There are two possible kinds of proclock tags: a transaction (identified
* There are two possible kinds of proclock owners: a transaction (identified
* both by the PGPROC of the backend running it, and the xact's own ID) and
* a session (identified by backend PGPROC, with XID = InvalidTransactionId).
*
@ -177,10 +177,10 @@ typedef struct LOCK
* under several different XIDs at once (including session locks). We treat
* such locks as never conflicting (a backend can never block itself).
*
* The holding[] array counts the granted locks (of each type) represented
* by this proclock. Note that there will be a proclock object, possibly with
* zero holding[], for any lock that the process is currently waiting on.
* Otherwise, proclock objects whose counts have gone to zero are recycled
* The holdMask field shows the already-granted locks represented by this
* proclock. Note that there will be a proclock object, possibly with
* zero holdMask, for any lock that the process is currently waiting on.
* Otherwise, proclock objects whose holdMasks are zero are recycled
* as soon as convenient.
*
* Each PROCLOCK object is linked into lists for both the associated LOCK
@ -202,8 +202,7 @@ typedef struct PROCLOCK
PROCLOCKTAG tag; /* unique identifier of proclock object */
/* data */
int holding[MAX_LOCKMODES]; /* count of locks currently held */
int nHolding; /* total of holding[] array */
LOCKMASK holdMask; /* bitmask for lock types currently held */
SHM_QUEUE lockLink; /* list link for lock's list of proclocks */
SHM_QUEUE procLink; /* list link for process's list of
* proclocks */
@ -212,6 +211,49 @@ typedef struct PROCLOCK
#define PROCLOCK_LOCKMETHOD(proclock) \
(((LOCK *) MAKE_PTR((proclock).tag.lock))->tag.lockmethodid)
/*
* Each backend also maintains a local hash table with information about each
* lock it is currently interested in. In particular the local table counts
* the number of times that lock has been acquired. This allows multiple
* requests for the same lock to be executed without additional accesses to
* shared memory. We also track the number of lock acquisitions per
* ResourceOwner, so that we can release just those locks belonging to a
* particular ResourceOwner.
*/
typedef struct LOCALLOCKTAG
{
LOCKTAG lock; /* identifies the lockable object */
TransactionId xid; /* xact ID, or InvalidTransactionId */
LOCKMODE mode; /* lock mode for this table entry */
} LOCALLOCKTAG;
typedef struct LOCALLOCKOWNER
{
/*
* Note: owner can be NULL to indicate a non-transactional lock.
* Must use a forward struct reference to avoid circularity.
*/
struct ResourceOwnerData *owner;
int nLocks; /* # of times held by this owner */
} LOCALLOCKOWNER;
typedef struct LOCALLOCK
{
/* tag */
LOCALLOCKTAG tag; /* unique identifier of locallock entry */
/* data */
LOCK *lock; /* associated LOCK object in shared mem */
PROCLOCK *proclock; /* associated PROCLOCK object in shmem */
int nLocks; /* total number of times lock is held */
int numLockOwners; /* # of relevant ResourceOwners */
int maxLockOwners; /* allocated size of array */
LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */
} LOCALLOCK;
#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.lockmethodid)
/*
* This struct holds information passed from lmgr internals to the lock
* listing user-level functions (lockfuncs.c). For each PROCLOCK in the
@ -229,7 +271,6 @@ typedef struct
LOCK *locks;
} LockData;
extern int NumLockMethods;
/*
* function prototypes
@ -244,13 +285,15 @@ extern bool LockAcquire(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
TransactionId xid, LOCKMODE lockmode, bool dontWait);
extern bool LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
TransactionId xid, LOCKMODE lockmode);
extern bool LockReleaseAll(LOCKMETHODID lockmethodid, PGPROC *proc,
bool allxids);
extern bool LockReleaseAll(LOCKMETHODID lockmethodid, bool allxids);
extern void LockReleaseCurrentOwner(void);
extern void LockReassignCurrentOwner(void);
extern int LockCheckConflicts(LockMethod lockMethodTable,
LOCKMODE lockmode,
LOCK *lock, PROCLOCK *proclock, PGPROC *proc,
int *myHolding);
extern void GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode);
extern void GrantAwaitedLock(void);
extern void RemoveFromWaitQueue(PGPROC *proc);
extern int LockShmemSize(int maxBackends);
extern bool DeadLockCheck(PGPROC *proc);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.72 2004/08/01 17:32:21 tgl Exp $
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.73 2004/08/27 17:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -78,14 +78,14 @@ struct PGPROC
struct PGPROC *lwWaitLink; /* next waiter for same LW lock */
/* Info about lock the process is currently waiting for, if any. */
/* waitLock and waitHolder are NULL if not currently waiting. */
/* waitLock and waitProcLock are NULL if not currently waiting. */
LOCK *waitLock; /* Lock object we're sleeping on ... */
PROCLOCK *waitHolder; /* Per-holder info for awaited lock */
PROCLOCK *waitProcLock; /* Per-holder info for awaited lock */
LOCKMODE waitLockMode; /* type of lock we're waiting for */
LOCKMASK heldLocks; /* bitmask for lock types already held on
* this lock object by this backend */
SHM_QUEUE procHolders; /* list of PROCLOCK objects for locks held
SHM_QUEUE procLocks; /* list of PROCLOCK objects for locks held
* or awaited by this backend */
struct XidCache subxids; /* cache for subtransaction XIDs */

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.1 2004/07/17 03:31:47 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.2 2004/08/27 17:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,7 +20,6 @@
#define RESOWNER_H
#include "storage/buf.h"
#include "storage/lock.h"
#include "utils/catcache.h"
#include "utils/rel.h"
@ -76,6 +75,7 @@ extern void ResourceOwnerRelease(ResourceOwner owner,
bool isCommit,
bool isTopLevel);
extern void ResourceOwnerDelete(ResourceOwner owner);
extern ResourceOwner ResourceOwnerGetParent(ResourceOwner owner);
extern void ResourceOwnerNewParent(ResourceOwner owner,
ResourceOwner newparent);
extern void RegisterResourceReleaseCallback(ResourceReleaseCallback callback,
@ -88,17 +88,6 @@ extern void ResourceOwnerEnlargeBuffers(ResourceOwner owner);
extern void ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer);
extern void ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer);
/* support for lock management */
extern void ResourceOwnerEnlargeLocks(ResourceOwner owner);
extern void ResourceOwnerRememberLock(ResourceOwner owner,
LOCKTAG *locktag,
TransactionId xid,
LOCKMODE lockmode);
extern void ResourceOwnerForgetLock(ResourceOwner owner,
LOCKTAG *locktag,
TransactionId xid,
LOCKMODE lockmode);
/* support for catcache refcount management */
extern void ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner);
extern void ResourceOwnerRememberCatCacheRef(ResourceOwner owner,