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
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
#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);
{
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);
}
{
return LockReleaseAll(USER_LOCKMETHOD, true);
}
-
-/* end of file */
-
-/*
- * Local Variables:
- * tab-width: 4
- * c-indent-level: 4
- * c-basic-offset: 4
- * End:
- */
#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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
*/
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
* 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
*
*
* 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
needLock = !RELATION_IS_LOCAL(rel);
if (needLock)
- LockPage(rel, 0, ExclusiveLock);
+ LockRelationForExtension(rel, ExclusiveLock);
buf = ReadBuffer(rel, P_NEW);
* 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);
-$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
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
*
*
* 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:
*
}
#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.
*/
DeadLockReport(void)
{
StringInfoData buf;
+ StringInfoData buf2;
int i;
initStringInfo(&buf);
+ initStringInfo(&buf2);
+
for (i = 0; i < nDeadlockDetails; i++)
{
DEADLOCK_INFO *info = &deadlockDetails[i];
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),
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "utils/inval.h"
-static LOCKMASK LockConflicts[] = {
+/*
+ * This conflict table defines the semantics of the various lock modes.
+ */
+static const LOCKMASK LockConflicts[] = {
0,
/* AccessShareLock */
static LOCKMETHODID LockTableId = INVALID_LOCKMETHOD;
+
/*
* Create the lock table described by LockConflicts
*/
#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
}
{
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))
{
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))
{
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);
}
{
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))
{
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))
{
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);
{
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);
}
{
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))
{
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);
}
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");
*
*
* 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
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));
}
{
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,
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,
* 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
* 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.
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];
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];
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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
{ 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 */
* 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
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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;
/*
* 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:
int nGranted; /* total of granted[] array */
} LOCK;
-#define LOCK_LOCKMETHOD(lock) ((lock).tag.lockmethodid)
+#define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid)
/*
} 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
LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */
} LOCALLOCK;
-#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.lockmethodid)
+#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid)
/*