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.
This commit is contained in:
Tom Lane 2005-04-29 22:28:24 +00:00
parent 32d3b47e6f
commit 3a694bb0a1
14 changed files with 347 additions and 255 deletions

View File

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

View File

@ -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:
*/

View File

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

View File

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

View File

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

View File

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

View File

@ -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,28 +926,19 @@ DeadLockReport(void)
if (i > 0)
appendStringInfoChar(&buf, '\n');
if (info->locktag.relId == XactLockTableId && info->locktag.dbId == 0)
{
/* Lock is for transaction ID */
/* 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 transaction %u; blocked by process %d."),
_("Process %d waits for %s on %s; blocked by process %d."),
info->pid,
GetLockmodeName(info->lockmode),
info->locktag.objId.xid,
buf2.data,
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);
}
}
ereport(ERROR,
(errcode(ERRCODE_T_R_DEADLOCK_DETECTED),
errmsg("deadlock detected"),

View File

@ -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");

View File

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

View File

@ -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 */
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.objId.xid);
}
else
{
/* Lock is for a relation */
values[0] = ObjectIdGetDatum(lock->tag.relId);
values[1] = ObjectIdGetDatum(lock->tag.dbId);
nulls[2] = 'n';
values[2] = TransactionIdGetDatum(lock->tag.locktag_field1);
break;
default:
/* XXX Ignore all other lock types for now */
continue;
}
values[3] = Int32GetDatum(proc->pid);

View File

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

View File

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

View File

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

View File

@ -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,26 +104,99 @@ 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;
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;
/*
* offnum should be part of objId union above, but doing that would
* increase sizeof(LOCKTAG) due to padding. Currently used by
* userlocks only.
* 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!
*/
OffsetNumber offnum;
#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)
LOCKMETHODID lockmethodid; /* needed by userlocks */
} LOCKTAG;
#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)
/*
@ -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)
/*