diff --git a/contrib/userlock/README.user_locks b/contrib/userlock/README.user_locks index 48acdd1d5f..5e61d4eea0 100644 --- a/contrib/userlock/README.user_locks +++ b/contrib/userlock/README.user_locks @@ -35,11 +35,11 @@ It could also be done with a begin/end block but in this case the entire table would be locked by Postgres and it is not acceptable to do this for a long period because other transactions would block completely. -The generic user locks use two values, group and id, to identify a lock, -which correspond to ip_posid and ip_blkid of an ItemPointerData. -Group is a 16 bit value while id is a 32 bit integer which could also be -an oid. The oid user lock functions, which take only an oid as argument, -use a group equal to 0. +The generic user locks use two values, group and id, to identify a lock. +Each of these are 32-bit integers. + +The oid user lock functions, which take only an OID as argument, store the +OID as "id" with a group equal to 0. The meaning of group and id is defined by the application. The user lock code just takes two numbers and tells you if the corresponding @@ -47,7 +47,9 @@ entity has been successfully locked. What this means is up to you. My suggestion is that you use the group to identify an area of your application and the id to identify an object in this area. -Or you can just lock the oid of the tuples which are by definition unique. + +In all cases, user locks are local to individual databases within an +installation. Note also that a process can acquire more than one lock on the same entity and it must release the lock the corresponding number of times. This can diff --git a/contrib/userlock/user_locks.c b/contrib/userlock/user_locks.c index 3dee92ea31..207610084b 100644 --- a/contrib/userlock/user_locks.c +++ b/contrib/userlock/user_locks.c @@ -18,16 +18,20 @@ #include "user_locks.h" +#define SET_LOCKTAG_USERLOCK(locktag,id1,id2) \ + ((locktag).locktag_field1 = (id1), \ + (locktag).locktag_field2 = (id2), \ + (locktag).locktag_field3 = MyDatabaseId, \ + (locktag).locktag_field4 = 0, \ + (locktag).locktag_type = LOCKTAG_USERLOCK) + + int user_lock(uint32 id1, uint32 id2, LOCKMODE lockmode) { LOCKTAG tag; - memset(&tag, 0, sizeof(LOCKTAG)); - tag.dbId = MyDatabaseId; - tag.relId = 0; - tag.objId.blkno = (BlockNumber) id2; - tag.offnum = (OffsetNumber) (id1 & 0xffff); + SET_LOCKTAG_USERLOCK(tag, id1, id2); return LockAcquire(USER_LOCKMETHOD, &tag, InvalidTransactionId, lockmode, true); @@ -38,11 +42,7 @@ user_unlock(uint32 id1, uint32 id2, LOCKMODE lockmode) { LOCKTAG tag; - memset(&tag, 0, sizeof(LOCKTAG)); - tag.dbId = MyDatabaseId; - tag.relId = 0; - tag.objId.blkno = (BlockNumber) id2; - tag.offnum = (OffsetNumber) (id1 & 0xffff); + SET_LOCKTAG_USERLOCK(tag, id1, id2); return LockRelease(USER_LOCKMETHOD, &tag, InvalidTransactionId, lockmode); } @@ -77,13 +77,3 @@ user_unlock_all(void) { return LockReleaseAll(USER_LOCKMETHOD, true); } - -/* end of file */ - -/* - * Local Variables: - * tab-width: 4 - * c-indent-level: 4 - * c-basic-offset: 4 - * End: - */ diff --git a/contrib/userlock/user_locks.h b/contrib/userlock/user_locks.h index 526e864661..e00735d693 100644 --- a/contrib/userlock/user_locks.h +++ b/contrib/userlock/user_locks.h @@ -1,19 +1,12 @@ #ifndef USER_LOCKS_H #define USER_LOCKS_H -int user_lock(unsigned int id1, unsigned int id2, LOCKMODE lockmode); -int user_unlock(unsigned int id1, unsigned int id2, LOCKMODE lockmode); -int user_write_lock(unsigned int id1, unsigned int id2); -int user_write_unlock(unsigned int id1, unsigned int id2); -int user_write_lock_oid(Oid oid); -int user_write_unlock_oid(Oid oid); -int user_unlock_all(void); -#endif +extern int user_lock(uint32 id1, uint32 id2, LOCKMODE lockmode); +extern int user_unlock(uint32 id1, uint32 id2, LOCKMODE lockmode); +extern int user_write_lock(uint32 id1, uint32 id2); +extern int user_write_unlock(uint32 id1, uint32 id2); +extern int user_write_lock_oid(Oid oid); +extern int user_write_unlock_oid(Oid oid); +extern int user_unlock_all(void); -/* - * Local Variables: - * tab-width: 4 - * c-indent-level: 4 - * c-basic-offset: 4 - * End: - */ +#endif diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 27af323ed1..db0bda6699 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 2b82a87a1d..222ed76435 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 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: * @@ -836,6 +836,69 @@ 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. */ @@ -843,9 +906,12 @@ 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]; diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index e6e14647da..6878662b79 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 a58f1d886b..ddca993a68 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 9ffc79ad8c..e08d6d60d3 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 8d63294f5c..4d1027c174 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 7f67a3ac3a..b1744325ff 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 enum LockTagType +{ + 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 */ + /* + * 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. + */ + LOCKTAG_USERLOCK /* reserved for contrib/userlock */ + /* ID info for a userlock is defined by user_locks.c */ +} LockTagType; + +/* + * 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 { - Oid relId; - Oid dbId; - union - { - BlockNumber blkno; - TransactionId xid; - } objId; - - /* - * 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; - - LOCKMETHODID lockmethodid; /* needed by userlocks */ + 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) /*