diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2005-04-29 22:28:24 +0000 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2005-04-29 22:28:24 +0000 |
| commit | 3a694bb0a16fea1662f1ffd31506a72effdd4a93 (patch) | |
| tree | 50bbf16b3117aada49b2709f524b3bdcf1a36815 /src/backend/storage/lmgr | |
| parent | 32d3b47e6f05c7137debddb68730a25fe1bb0cd6 (diff) | |
| download | postgresql-3a694bb0a16fea1662f1ffd31506a72effdd4a93.tar.gz | |
Restructure LOCKTAG as per discussions of a couple months ago.
Essentially, we shoehorn in a lockable-object-type field by taking
a byte away from the lockmethodid, which can surely fit in one byte
instead of two. This allows less artificial definitions of all the
other fields of LOCKTAG; we can get rid of the special pg_xactlock
pseudo-relation, and also support locks on individual tuples and
general database objects (including shared objects). None of those
possibilities are actually exploited just yet, however.
I removed pg_xactlock from pg_class, but did not force initdb for
that change. At this point, relkind 's' (SPECIAL) is unused and
could be removed entirely.
Diffstat (limited to 'src/backend/storage/lmgr')
| -rw-r--r-- | src/backend/storage/lmgr/README | 34 | ||||
| -rw-r--r-- | src/backend/storage/lmgr/deadlock.c | 101 | ||||
| -rw-r--r-- | src/backend/storage/lmgr/lmgr.c | 124 | ||||
| -rw-r--r-- | src/backend/storage/lmgr/lock.c | 84 |
4 files changed, 194 insertions, 149 deletions
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README index d8489ab121..6e5c36e137 100644 --- a/src/backend/storage/lmgr/README +++ b/src/backend/storage/lmgr/README @@ -1,4 +1,4 @@ -$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.15 2004/08/27 17:07:41 tgl Exp $ +$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.16 2005/04/29 22:28:24 tgl Exp $ LOCKING OVERVIEW @@ -74,30 +74,14 @@ The lock manager's LOCK objects contain: tag - The key fields that are used for hashing locks in the shared memory - lock hash table. This is declared as a separate struct to ensure that - we always zero out the correct number of bytes. It is critical that - any alignment-padding bytes the compiler might insert in the struct - be zeroed out, else the hash computation will be random. - - tag.relId - - Uniquely identifies the relation that the lock corresponds to. - - tag.dbId - - Uniquely identifies the database in which the relation lives. If - this is a shared system relation (e.g. pg_database) the dbId must - be set to 0. - - tag.objId - - Uniquely identifies the block/page within the relation and the - tuple within the block. If we are setting a table level lock - both the blockId and tupleId (in an item pointer this is called - the position) are set to invalid, if it is a page level lock the - blockId is valid, while the tupleId is still invalid. Finally if - this is a tuple level lock (we currently never do this) then both - the blockId and tupleId are set to valid specifications. This is - how we get the appearance of a multi-level lock table while using - only a single table (see Gray's paper on 2 phase locking if - you are puzzled about how multi-level lock tables work). + lock hash table. The contents of the tag essentially define an + individual lockable object. See include/storage/lock.h for details + about the supported types of lockable objects. This is declared as + a separate struct to ensure that we always zero out the correct number + of bytes. It is critical that any alignment-padding bytes the compiler + might insert in the struct be zeroed out, else the hash computation + will be random. (Currently, we are careful to define struct LOCKTAG + so that there are no padding bytes.) grantMask - This bitmask indicates what types of locks are currently held on the diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c index 7b4ca899b6..7edabff6dd 100644 --- a/src/backend/storage/lmgr/deadlock.c +++ b/src/backend/storage/lmgr/deadlock.c @@ -12,7 +12,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.33 2005/02/22 04:36:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.34 2005/04/29 22:28:24 tgl Exp $ * * Interface: * @@ -837,15 +837,81 @@ PrintLockQueue(LOCK *lock, const char *info) #endif /* + * Append a description of a lockable object to buf. + * + * XXX probably this should be exported from lmgr.c or some such place. + */ +static void +DescribeLockTag(StringInfo buf, const LOCKTAG *lock) +{ + switch (lock->locktag_type) + { + case LOCKTAG_RELATION: + appendStringInfo(buf, + _("relation %u of database %u"), + lock->locktag_field2, + lock->locktag_field1); + break; + case LOCKTAG_RELATION_EXTEND: + appendStringInfo(buf, + _("extension of relation %u of database %u"), + lock->locktag_field2, + lock->locktag_field1); + break; + case LOCKTAG_PAGE: + appendStringInfo(buf, + _("page %u of relation %u of database %u"), + lock->locktag_field3, + lock->locktag_field2, + lock->locktag_field1); + break; + case LOCKTAG_TUPLE: + appendStringInfo(buf, + _("tuple (%u,%u) of relation %u of database %u"), + lock->locktag_field3, + lock->locktag_field4, + lock->locktag_field2, + lock->locktag_field1); + break; + case LOCKTAG_TRANSACTION: + appendStringInfo(buf, + _("transaction %u"), + lock->locktag_field1); + break; + case LOCKTAG_OBJECT: + appendStringInfo(buf, + _("object %u of class %u of database %u"), + lock->locktag_field3, + lock->locktag_field2, + lock->locktag_field1); + break; + case LOCKTAG_USERLOCK: + appendStringInfo(buf, + _("user lock [%u,%u]"), + lock->locktag_field1, + lock->locktag_field2); + break; + default: + appendStringInfo(buf, + _("unknown locktag type %d"), + lock->locktag_type); + break; + } +} + +/* * Report a detected deadlock, with available details. */ void DeadLockReport(void) { StringInfoData buf; + StringInfoData buf2; int i; initStringInfo(&buf); + initStringInfo(&buf2); + for (i = 0; i < nDeadlockDetails; i++) { DEADLOCK_INFO *info = &deadlockDetails[i]; @@ -860,27 +926,18 @@ DeadLockReport(void) if (i > 0) appendStringInfoChar(&buf, '\n'); - if (info->locktag.relId == XactLockTableId && info->locktag.dbId == 0) - { - /* Lock is for transaction ID */ - appendStringInfo(&buf, - _("Process %d waits for %s on transaction %u; blocked by process %d."), - info->pid, - GetLockmodeName(info->lockmode), - info->locktag.objId.xid, - nextpid); - } - else - { - /* Lock is for a relation */ - appendStringInfo(&buf, - _("Process %d waits for %s on relation %u of database %u; blocked by process %d."), - info->pid, - GetLockmodeName(info->lockmode), - info->locktag.relId, - info->locktag.dbId, - nextpid); - } + /* reset buf2 to hold next object description */ + buf2.len = 0; + buf2.data[0] = '\0'; + + DescribeLockTag(&buf2, &info->locktag); + + appendStringInfo(&buf, + _("Process %d waits for %s on %s; blocked by process %d."), + info->pid, + GetLockmodeName(info->lockmode), + buf2.data, + nextpid); } ereport(ERROR, (errcode(ERRCODE_T_R_DEADLOCK_DETECTED), diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index c1a69401ce..d527724b9a 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.71 2004/12/31 22:01:05 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.72 2005/04/29 22:28:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,10 @@ #include "utils/inval.h" -static LOCKMASK LockConflicts[] = { +/* + * This conflict table defines the semantics of the various lock modes. + */ +static const LOCKMASK LockConflicts[] = { 0, /* AccessShareLock */ @@ -69,6 +72,7 @@ static LOCKMASK LockConflicts[] = { static LOCKMETHODID LockTableId = INVALID_LOCKMETHOD; + /* * Create the lock table described by LockConflicts */ @@ -96,11 +100,11 @@ InitLockTable(int maxBackends) #ifdef USER_LOCKS /* - * Allocate another tableId for long-term locks + * Allocate another tableId for user locks (same shared hashtable though) */ LongTermTableId = LockMethodTableRename(LockTableId); if (!LockMethodIsValid(LongTermTableId)) - elog(ERROR, "could not rename long-term lock table"); + elog(ERROR, "could not rename user lock table"); Assert(LongTermTableId == USER_LOCKMETHOD); #endif } @@ -133,10 +137,9 @@ LockRelation(Relation relation, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relation->rd_lockInfo.lockRelId.relId; - tag.dbId = relation->rd_lockInfo.lockRelId.dbId; - tag.objId.blkno = InvalidBlockNumber; + SET_LOCKTAG_RELATION(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId); if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(), lockmode, false)) @@ -167,10 +170,9 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relation->rd_lockInfo.lockRelId.relId; - tag.dbId = relation->rd_lockInfo.lockRelId.dbId; - tag.objId.blkno = InvalidBlockNumber; + SET_LOCKTAG_RELATION(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId); if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(), lockmode, true)) @@ -197,10 +199,9 @@ UnlockRelation(Relation relation, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relation->rd_lockInfo.lockRelId.relId; - tag.dbId = relation->rd_lockInfo.lockRelId.dbId; - tag.objId.blkno = InvalidBlockNumber; + SET_LOCKTAG_RELATION(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId); LockRelease(LockTableId, &tag, GetTopTransactionId(), lockmode); } @@ -222,10 +223,7 @@ LockRelationForSession(LockRelId *relid, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relid->relId; - tag.dbId = relid->dbId; - tag.objId.blkno = InvalidBlockNumber; + SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); if (!LockAcquire(LockTableId, &tag, InvalidTransactionId, lockmode, false)) @@ -240,30 +238,65 @@ UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relid->relId; - tag.dbId = relid->dbId; - tag.objId.blkno = InvalidBlockNumber; + SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); LockRelease(LockTableId, &tag, InvalidTransactionId, lockmode); } /* + * LockRelationForExtension + * + * This lock tag is used to interlock addition of pages to relations. + * We need such locking because bufmgr/smgr definition of P_NEW is not + * race-condition-proof. + * + * We assume the caller is already holding some type of regular lock on + * the relation, so no AcceptInvalidationMessages call is needed here. + */ +void +LockRelationForExtension(Relation relation, LOCKMODE lockmode) +{ + LOCKTAG tag; + + SET_LOCKTAG_RELATION_EXTEND(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId); + + if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(), + lockmode, false)) + elog(ERROR, "LockAcquire failed"); +} + +/* + * UnlockRelationForExtension + */ +void +UnlockRelationForExtension(Relation relation, LOCKMODE lockmode) +{ + LOCKTAG tag; + + SET_LOCKTAG_RELATION_EXTEND(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId); + + LockRelease(LockTableId, &tag, GetTopTransactionId(), lockmode); +} + +/* * LockPage * * Obtain a page-level lock. This is currently used by some index access - * methods to lock index pages. For heap relations, it is used only with - * blkno == 0 to signify locking the relation for extension. + * methods to lock individual index pages. */ void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relation->rd_lockInfo.lockRelId.relId; - tag.dbId = relation->rd_lockInfo.lockRelId.dbId; - tag.objId.blkno = blkno; + SET_LOCKTAG_PAGE(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId, + blkno); if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(), lockmode, false)) @@ -281,10 +314,10 @@ ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relation->rd_lockInfo.lockRelId.relId; - tag.dbId = relation->rd_lockInfo.lockRelId.dbId; - tag.objId.blkno = blkno; + SET_LOCKTAG_PAGE(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId, + blkno); return LockAcquire(LockTableId, &tag, GetTopTransactionId(), lockmode, true); @@ -298,10 +331,10 @@ UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = relation->rd_lockInfo.lockRelId.relId; - tag.dbId = relation->rd_lockInfo.lockRelId.dbId; - tag.objId.blkno = blkno; + SET_LOCKTAG_PAGE(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId, + blkno); LockRelease(LockTableId, &tag, GetTopTransactionId(), lockmode); } @@ -318,10 +351,7 @@ XactLockTableInsert(TransactionId xid) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = XactLockTableId; - tag.dbId = InvalidOid; /* xids are globally unique */ - tag.objId.xid = xid; + SET_LOCKTAG_TRANSACTION(tag, xid); if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(), ExclusiveLock, false)) @@ -341,10 +371,7 @@ XactLockTableDelete(TransactionId xid) { LOCKTAG tag; - MemSet(&tag, 0, sizeof(tag)); - tag.relId = XactLockTableId; - tag.dbId = InvalidOid; /* xids are globally unique */ - tag.objId.xid = xid; + SET_LOCKTAG_TRANSACTION(tag, xid); LockRelease(LockTableId, &tag, GetTopTransactionId(), ExclusiveLock); } @@ -372,10 +399,7 @@ XactLockTableWait(TransactionId xid) Assert(TransactionIdIsValid(xid)); Assert(!TransactionIdEquals(xid, myxid)); - MemSet(&tag, 0, sizeof(tag)); - tag.relId = XactLockTableId; - tag.dbId = InvalidOid; - tag.objId.xid = xid; + SET_LOCKTAG_TRANSACTION(tag, xid); if (!LockAcquire(LockTableId, &tag, myxid, ShareLock, false)) elog(ERROR, "LockAcquire failed"); diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 7c7b2b1abd..576fc20529 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.149 2005/04/13 18:54:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.150 2005/04/29 22:28:24 tgl Exp $ * * NOTES * Outside modules can create a lock table and acquire/release @@ -108,10 +108,11 @@ inline static bool LOCK_DEBUG_ENABLED(const LOCK *lock) { return - (((LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD && Trace_locks) - || (LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD && Trace_userlocks)) - && (lock->tag.relId >= (Oid) Trace_lock_oidmin)) - || (Trace_lock_table && (lock->tag.relId == Trace_lock_table)); + (((Trace_locks && LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD) + || (Trace_userlocks && LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD)) + && ((Oid) lock->tag.locktag_field2 >= (Oid) Trace_lock_oidmin)) + || (Trace_lock_table + && (lock->tag.locktag_field2 == Trace_lock_table)); } @@ -120,12 +121,14 @@ LOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type) { if (LOCK_DEBUG_ENABLED(lock)) elog(LOG, - "%s: lock(%lx) tbl(%d) rel(%u) db(%u) obj(%u) grantMask(%x) " + "%s: lock(%lx) id(%u,%u,%u,%u,%u,%u) grantMask(%x) " "req(%d,%d,%d,%d,%d,%d,%d)=%d " "grant(%d,%d,%d,%d,%d,%d,%d)=%d wait(%d) type(%s)", where, MAKE_OFFSET(lock), - lock->tag.lockmethodid, lock->tag.relId, lock->tag.dbId, - lock->tag.objId.blkno, lock->grantMask, + lock->tag.locktag_field1, lock->tag.locktag_field2, + lock->tag.locktag_field3, lock->tag.locktag_field4, + lock->tag.locktag_type, lock->tag.locktag_lockmethodid, + lock->grantMask, lock->requested[1], lock->requested[2], lock->requested[3], lock->requested[4], lock->requested[5], lock->requested[6], lock->requested[7], lock->nRequested, @@ -139,14 +142,9 @@ LOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type) inline static void PROCLOCK_PRINT(const char *where, const PROCLOCK *proclockP) { - if ( - (((PROCLOCK_LOCKMETHOD(*proclockP) == DEFAULT_LOCKMETHOD && Trace_locks) - || (PROCLOCK_LOCKMETHOD(*proclockP) == USER_LOCKMETHOD && Trace_userlocks)) - && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId >= (Oid) Trace_lock_oidmin)) - || (Trace_lock_table && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId == Trace_lock_table)) - ) + if (LOCK_DEBUG_ENABLED((LOCK *) MAKE_PTR(proclockP->tag.lock))) elog(LOG, - "%s: proclock(%lx) lock(%lx) tbl(%d) proc(%lx) xid(%u) hold(%x)", + "%s: proclock(%lx) lock(%lx) method(%u) proc(%lx) xid(%u) hold(%x)", where, MAKE_OFFSET(proclockP), proclockP->tag.lock, PROCLOCK_LOCKMETHOD(*(proclockP)), proclockP->tag.proc, proclockP->tag.xid, @@ -346,13 +344,9 @@ LockMethodTableInit(const char *tabName, * LockMethodTableRename -- allocate another lockmethod ID to the same * lock table. * - * NOTES: Both the lock module and the lock chain (lchain.c) - * module use table id's to distinguish between different - * kinds of locks. Short term and long term locks look - * the same to the lock table, but are handled differently - * by the lock chain manager. This function allows the - * client to use different lockmethods when acquiring/releasing - * short term and long term locks, yet store them all in one hashtable. + * NOTES: This function makes it possible to have different lockmethodids, + * and hence different locking semantics, while still storing all + * the data in one shared-memory hashtable. */ LOCKMETHODID @@ -404,33 +398,16 @@ LockMethodTableRename(LOCKMETHODID lockmethodid) * the lock. While the lock is active other clients can still * read and write the tuple but they can be aware that it has * been locked at the application level by someone. - * User locks use lock tags made of an uint16 and an uint32, for - * example 0 and a tuple oid, or any other arbitrary pair of - * numbers following a convention established by the application. - * In this sense tags don't refer to tuples or database entities. + * * User locks and normal locks are completely orthogonal and - * they don't interfere with each other, so it is possible - * to acquire a normal lock on an user-locked tuple or user-lock - * a tuple for which a normal write lock already exists. + * they don't interfere with each other. + * * User locks are always non blocking, therefore they are never * acquired if already held by another process. They must be * released explicitly by the application but they are released * automatically when a backend terminates. * They are indicated by a lockmethod 2 which is an alias for the - * normal lock table, and are distinguished from normal locks - * by the following differences: - * - * normal lock user lock - * - * lockmethodid 1 2 - * tag.dbId database oid database oid - * tag.relId rel oid or 0 0 - * tag.objId block id lock id2 - * or xact id - * tag.offnum 0 lock id1 - * proclock.xid xid or 0 0 - * persistence transaction user or backend - * or backend + * normal lock table. * * The lockmode parameter can have the same values for normal locks * although probably only WRITE_LOCK can have some practical use. @@ -456,13 +433,14 @@ LockAcquire(LOCKMETHODID lockmethodid, LOCKTAG *locktag, int i; #ifdef LOCK_DEBUG - if (lockmethodid == USER_LOCKMETHOD && Trace_userlocks) - elog(LOG, "LockAcquire: user lock [%u] %s", - locktag->objId.blkno, lock_mode_names[lockmode]); + if (Trace_userlocks && lockmethodid == USER_LOCKMETHOD) + elog(LOG, "LockAcquire: user lock [%u,%u] %s", + locktag->locktag_field1, locktag->locktag_field2, + lock_mode_names[lockmode]); #endif - /* ???????? This must be changed when short term locks will be used */ - locktag->lockmethodid = lockmethodid; + /* ugly */ + locktag->locktag_lockmethodid = lockmethodid; Assert(lockmethodid < NumLockMethods); lockMethodTable = LockMethods[lockmethodid]; @@ -1231,12 +1209,14 @@ LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag, bool wakeupNeeded; #ifdef LOCK_DEBUG - if (lockmethodid == USER_LOCKMETHOD && Trace_userlocks) - elog(LOG, "LockRelease: user lock tag [%u] %d", locktag->objId.blkno, lockmode); + if (Trace_userlocks && lockmethodid == USER_LOCKMETHOD) + elog(LOG, "LockRelease: user lock [%u,%u] %s", + locktag->locktag_field1, locktag->locktag_field2, + lock_mode_names[lockmode]); #endif - /* ???????? This must be changed when short term locks will be used */ - locktag->lockmethodid = lockmethodid; + /* ugly */ + locktag->locktag_lockmethodid = lockmethodid; Assert(lockmethodid < NumLockMethods); lockMethodTable = LockMethods[lockmethodid]; |
