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 | |
parent | 32d3b47e6f05c7137debddb68730a25fe1bb0cd6 (diff) | |
download | postgresql-3a694bb0a16fea1662f1ffd31506a72effdd4a93.tar.gz postgresql-3a694bb0a16fea1662f1ffd31506a72effdd4a93.zip |
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')
-rw-r--r-- | src/backend/access/heap/hio.c | 11 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtpage.c | 6 | ||||
-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 | ||||
-rw-r--r-- | src/backend/utils/adt/lockfuncs.c | 32 | ||||
-rw-r--r-- | src/include/catalog/pg_attribute.h | 12 | ||||
-rw-r--r-- | src/include/catalog/pg_class.h | 7 | ||||
-rw-r--r-- | src/include/storage/lmgr.h | 8 | ||||
-rw-r--r-- | src/include/storage/lock.h | 112 |
11 files changed, 319 insertions, 212 deletions
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 27af323ed11..db0bda6699b 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.54 2004/12/31 21:59:16 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.55 2005/04/29 22:28:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,11 +79,6 @@ RelationPutHeapTuple(Relation relation, * happen if space is freed in that page after heap_update finds there's not * enough there). In that case, the page will be pinned and locked only once. * - * Note that we use LockPage(rel, 0) to lock relation for extension. - * We can do this as long as in all other places we use page-level locking - * for indices only. Alternatively, we could define pseudo-table as - * we do for transactions with XactLockTable. - * * ereport(ERROR) is allowed here, so this routine *must* be called * before any (unlogged) changes are made in buffer pool. */ @@ -235,7 +230,7 @@ RelationGetBufferForTuple(Relation relation, Size len, needLock = !RELATION_IS_LOCAL(relation); if (needLock) - LockPage(relation, 0, ExclusiveLock); + LockRelationForExtension(relation, ExclusiveLock); /* * XXX This does an lseek - rather expensive - but at the moment it is @@ -251,7 +246,7 @@ RelationGetBufferForTuple(Relation relation, Size len, * extend the relation some more. */ if (needLock) - UnlockPage(relation, 0, ExclusiveLock); + UnlockRelationForExtension(relation, ExclusiveLock); /* * We can be certain that locking the otherBuffer first is OK, since diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 2b82a87a1d4..222ed764357 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.82 2005/03/22 06:17:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.83 2005/04/29 22:28:24 tgl Exp $ * * NOTES * Postgres btree pages look like ordinary relation pages. The opaque @@ -487,7 +487,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access) needLock = !RELATION_IS_LOCAL(rel); if (needLock) - LockPage(rel, 0, ExclusiveLock); + LockRelationForExtension(rel, ExclusiveLock); buf = ReadBuffer(rel, P_NEW); @@ -496,7 +496,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access) * to extend the relation some more. */ if (needLock) - UnlockPage(rel, 0, ExclusiveLock); + UnlockRelationForExtension(rel, ExclusiveLock); /* Acquire appropriate buffer lock on new page */ LockBuffer(buf, access); diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README index d8489ab1211..6e5c36e1373 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 7b4ca899b62..7edabff6dd4 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 c1a69401ce1..d527724b9a6 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 7c7b2b1abdd..576fc205299 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]; diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index e6e14647daf..6878662b79f 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -6,7 +6,7 @@ * Copyright (c) 2002-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.16 2005/01/01 05:43:07 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.17 2005/04/29 22:28:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -155,20 +155,24 @@ pg_lock_status(PG_FUNCTION_ARGS) MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); - if (lock->tag.relId == XactLockTableId && lock->tag.dbId == 0) + switch (lock->tag.locktag_type) { - /* Lock is for transaction ID */ - nulls[0] = 'n'; - nulls[1] = 'n'; - values[2] = TransactionIdGetDatum(lock->tag.objId.xid); - } - else - { - /* Lock is for a relation */ - values[0] = ObjectIdGetDatum(lock->tag.relId); - values[1] = ObjectIdGetDatum(lock->tag.dbId); - nulls[2] = 'n'; - + case LOCKTAG_RELATION: + case LOCKTAG_RELATION_EXTEND: + case LOCKTAG_PAGE: + case LOCKTAG_TUPLE: + values[0] = ObjectIdGetDatum(lock->tag.locktag_field2); + values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); + nulls[2] = 'n'; + break; + case LOCKTAG_TRANSACTION: + nulls[0] = 'n'; + nulls[1] = 'n'; + values[2] = TransactionIdGetDatum(lock->tag.locktag_field1); + break; + default: + /* XXX Ignore all other lock types for now */ + continue; } values[3] = Int32GetDatum(proc->pid); diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index a58f1d886b9..ddca993a68a 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.116 2005/04/14 01:38:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.117 2005/04/29 22:28:24 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -461,14 +461,4 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); { 0, {"indexprs"}, 25, -1, -1, 9, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \ { 0, {"indpred"}, 25, -1, -1, 10, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 } -/* ---------------- - * pg_xactlock - this is not a real relation, but is a placeholder - * to allow a relation OID to be used for transaction - * waits. We need a pg_xactlock entry in pg_class only to - * ensure that that OID can never be allocated to a real - * table; and this entry is just to link to that one. - * ---------------- - */ -DATA(insert ( 376 xactlockfoo 26 0 4 1 0 -1 -1 t p i t f f t 0)); - #endif /* PG_ATTRIBUTE_H */ diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 9ffc79ad8ca..e08d6d60d33 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.87 2005/04/14 01:38:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.88 2005/04/29 22:28:24 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -147,11 +147,6 @@ DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 DESCR(""); DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 25 0 0 0 0 0 t f f f _null_ )); DESCR(""); -DATA(insert OID = 376 ( pg_xactlock PGNSP 0 PGUID 0 0 1664 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ )); -DESCR(""); - -/* Xact lock pseudo-table */ -#define XactLockTableId 376 #define RELKIND_INDEX 'i' /* secondary index */ #define RELKIND_RELATION 'r' /* ordinary cataloged heap */ diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h index 8d63294f5cd..4d1027c174e 100644 --- a/src/include/storage/lmgr.h +++ b/src/include/storage/lmgr.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.46 2005/04/28 21:47:18 tgl Exp $ + * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.47 2005/04/29 22:28:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -51,7 +51,11 @@ extern void UnlockRelation(Relation relation, LOCKMODE lockmode); extern void LockRelationForSession(LockRelId *relid, LOCKMODE lockmode); extern void UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode); -/* Lock a page (mainly used for indexes) */ +/* Lock a relation for extension */ +extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode); +extern void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode); + +/* Lock a page (currently only used within indexes) */ extern void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); extern bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index 7f67a3ac3a9..b1744325ff2 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.84 2004/12/31 22:03:42 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.85 2005/04/29 22:28:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -58,7 +58,8 @@ typedef int LOCKMODE; /* * There is normally only one lock method, the default one. * If user locks are enabled, an additional lock method is present. - * Lock methods are identified by LOCKMETHODID. + * Lock methods are identified by LOCKMETHODID. (Despite the declaration as + * uint16, we are constrained to 256 lockmethods by the layout of LOCKTAG.) */ typedef uint16 LOCKMETHODID; @@ -103,27 +104,100 @@ typedef LockMethodData *LockMethod; /* * LOCKTAG is the key information needed to look up a LOCK item in the * lock hashtable. A LOCKTAG value uniquely identifies a lockable object. + * + * The LockTagType enum defines the different kinds of objects we can lock. + * We can handle up to 256 different LockTagTypes. */ -typedef struct LOCKTAG +typedef enum LockTagType { - Oid relId; - Oid dbId; - union - { - BlockNumber blkno; - TransactionId xid; - } objId; - + LOCKTAG_RELATION, /* whole relation */ + /* ID info for a relation is DB OID + REL OID; DB OID = 0 if shared */ + LOCKTAG_RELATION_EXTEND, /* the right to extend a relation */ + /* same ID info as RELATION */ + LOCKTAG_PAGE, /* one page of a relation */ + /* ID info for a page is RELATION info + BlockNumber */ + LOCKTAG_TUPLE, /* one physical tuple */ + /* ID info for a tuple is PAGE info + OffsetNumber */ + LOCKTAG_TRANSACTION, /* transaction (for waiting for xact done) */ + /* ID info for a transaction is its TransactionId */ + LOCKTAG_OBJECT, /* non-relation database object */ + /* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */ /* - * offnum should be part of objId union above, but doing that would - * increase sizeof(LOCKTAG) due to padding. Currently used by - * userlocks only. + * Note: object ID has same representation as in pg_depend and + * pg_description, but notice that we are constraining SUBID to 16 bits. + * Also, we use DB OID = 0 for shared objects such as tablespaces. */ - OffsetNumber offnum; + LOCKTAG_USERLOCK /* reserved for contrib/userlock */ + /* ID info for a userlock is defined by user_locks.c */ +} LockTagType; - LOCKMETHODID lockmethodid; /* needed by userlocks */ +/* + * The LOCKTAG struct is defined with malice aforethought to fit into 16 + * bytes with no padding. Note that this would need adjustment if we were + * to widen Oid, BlockNumber, or TransactionId to more than 32 bits. + * + * We include lockmethodid in the locktag so that a single hash table in + * shared memory can store locks of different lockmethods. For largely + * historical reasons, it's passed to the lock.c routines as a separate + * argument and then stored into the locktag. + */ +typedef struct LOCKTAG +{ + uint32 locktag_field1; /* a 32-bit ID field */ + uint32 locktag_field2; /* a 32-bit ID field */ + uint32 locktag_field3; /* a 32-bit ID field */ + uint16 locktag_field4; /* a 16-bit ID field */ + uint8 locktag_type; /* see enum LockTagType */ + uint8 locktag_lockmethodid; /* lockmethod indicator */ } LOCKTAG; +/* + * These macros define how we map logical IDs of lockable objects into + * the physical fields of LOCKTAG. Use these to set up LOCKTAG values, + * rather than accessing the fields directly. Note multiple eval of target! + */ +#define SET_LOCKTAG_RELATION(locktag,dboid,reloid) \ + ((locktag).locktag_field1 = (dboid), \ + (locktag).locktag_field2 = (reloid), \ + (locktag).locktag_field3 = 0, \ + (locktag).locktag_field4 = 0, \ + (locktag).locktag_type = LOCKTAG_RELATION) + +#define SET_LOCKTAG_RELATION_EXTEND(locktag,dboid,reloid) \ + ((locktag).locktag_field1 = (dboid), \ + (locktag).locktag_field2 = (reloid), \ + (locktag).locktag_field3 = 0, \ + (locktag).locktag_field4 = 0, \ + (locktag).locktag_type = LOCKTAG_RELATION_EXTEND) + +#define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \ + ((locktag).locktag_field1 = (dboid), \ + (locktag).locktag_field2 = (reloid), \ + (locktag).locktag_field3 = (blocknum), \ + (locktag).locktag_field4 = 0, \ + (locktag).locktag_type = LOCKTAG_PAGE) + +#define SET_LOCKTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \ + ((locktag).locktag_field1 = (dboid), \ + (locktag).locktag_field2 = (reloid), \ + (locktag).locktag_field3 = (blocknum), \ + (locktag).locktag_field4 = (offnum), \ + (locktag).locktag_type = LOCKTAG_TUPLE) + +#define SET_LOCKTAG_TRANSACTION(locktag,xid) \ + ((locktag).locktag_field1 = (xid), \ + (locktag).locktag_field2 = 0, \ + (locktag).locktag_field3 = 0, \ + (locktag).locktag_field4 = 0, \ + (locktag).locktag_type = LOCKTAG_TRANSACTION) + +#define SET_LOCKTAG_OBJECT(locktag,dboid,classoid,objoid,objsubid) \ + ((locktag).locktag_field1 = (dboid), \ + (locktag).locktag_field2 = (classoid), \ + (locktag).locktag_field3 = (objoid), \ + (locktag).locktag_field4 = (objsubid), \ + (locktag).locktag_type = LOCKTAG_OBJECT) + /* * Per-locked-object lock information: @@ -157,7 +231,7 @@ typedef struct LOCK int nGranted; /* total of granted[] array */ } LOCK; -#define LOCK_LOCKMETHOD(lock) ((lock).tag.lockmethodid) +#define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid) /* @@ -211,7 +285,7 @@ typedef struct PROCLOCK } PROCLOCK; #define PROCLOCK_LOCKMETHOD(proclock) \ - (((LOCK *) MAKE_PTR((proclock).tag.lock))->tag.lockmethodid) + LOCK_LOCKMETHOD(*((LOCK *) MAKE_PTR((proclock).tag.lock))) /* * Each backend also maintains a local hash table with information about each @@ -253,7 +327,7 @@ typedef struct LOCALLOCK LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */ } LOCALLOCK; -#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.lockmethodid) +#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid) /* |