1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-06 07:49:08 +03:00

Add transaction-level advisory locks.

They share the same locking namespace with the existing session-level
advisory locks, but they are automatically released at the end of the
current transaction and cannot be released explicitly via unlock
functions.

Marko Tiikkaja, reviewed by me.
This commit is contained in:
Itagaki Takahiro
2011-02-18 14:04:34 +09:00
parent 87bb2ade2c
commit 62c7bd31c8
14 changed files with 814 additions and 71 deletions

View File

@@ -130,7 +130,7 @@ static const LockMethodData default_lockmethod = {
static const LockMethodData user_lockmethod = {
AccessExclusiveLock, /* highest valid lock mode number */
false,
true,
LockConflicts,
lock_mode_names,
#ifdef LOCK_DEBUG
@@ -256,6 +256,7 @@ static uint32 proclock_hash(const void *key, Size keysize);
static void RemoveLocalLock(LOCALLOCK *locallock);
static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
static void ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner);
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
PROCLOCK *proclock, LockMethod lockMethodTable);
static void CleanUpLock(LOCK *lock, PROCLOCK *proclock,
@@ -1483,6 +1484,31 @@ LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
return TRUE;
}
/*
* LockReleaseSession -- Release all session locks of the specified lock method
* that are held by the current process.
*/
void
LockReleaseSession(LOCKMETHODID lockmethodid)
{
HASH_SEQ_STATUS status;
LOCALLOCK *locallock;
if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
hash_seq_init(&status, LockMethodLocalHash);
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
{
/* Ignore items that are not of the specified lock method */
if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid)
continue;
ReleaseLockForOwner(locallock, NULL);
}
}
/*
* LockReleaseAll -- Release all locks of the specified lock method that
* are held by the current process.
@@ -1679,8 +1705,6 @@ LockReleaseCurrentOwner(void)
{
HASH_SEQ_STATUS status;
LOCALLOCK *locallock;
LOCALLOCKOWNER *lockOwners;
int i;
hash_seq_init(&status, LockMethodLocalHash);
@@ -1690,38 +1714,51 @@ LockReleaseCurrentOwner(void)
if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
continue;
/* Scan to see if there are any locks belonging to current owner */
lockOwners = locallock->lockOwners;
for (i = locallock->numLockOwners - 1; i >= 0; i--)
ReleaseLockForOwner(locallock, CurrentResourceOwner);
}
}
/*
* Subroutine to release a lock belonging to the 'owner' if found.
* 'owner' can be NULL to release a session lock.
*/
static void
ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner)
{
int i;
LOCALLOCKOWNER *lockOwners;
/* Scan to see if there are any locks belonging to the owner */
lockOwners = locallock->lockOwners;
for (i = locallock->numLockOwners - 1; i >= 0; i--)
{
if (lockOwners[i].owner == owner)
{
if (lockOwners[i].owner == CurrentResourceOwner)
Assert(lockOwners[i].nLocks > 0);
if (lockOwners[i].nLocks < locallock->nLocks)
{
Assert(lockOwners[i].nLocks > 0);
if (lockOwners[i].nLocks < locallock->nLocks)
{
/*
* We will still hold this lock after forgetting this
* ResourceOwner.
*/
locallock->nLocks -= lockOwners[i].nLocks;
/* compact out unused slot */
locallock->numLockOwners--;
if (i < locallock->numLockOwners)
lockOwners[i] = lockOwners[locallock->numLockOwners];
}
else
{
Assert(lockOwners[i].nLocks == locallock->nLocks);
/* We want to call LockRelease just once */
lockOwners[i].nLocks = 1;
locallock->nLocks = 1;
if (!LockRelease(&locallock->tag.lock,
locallock->tag.mode,
false))
elog(WARNING, "LockReleaseCurrentOwner: failed??");
}
break;
/*
* We will still hold this lock after forgetting this
* ResourceOwner.
*/
locallock->nLocks -= lockOwners[i].nLocks;
/* compact out unused slot */
locallock->numLockOwners--;
if (i < locallock->numLockOwners)
lockOwners[i] = lockOwners[locallock->numLockOwners];
}
else
{
Assert(lockOwners[i].nLocks == locallock->nLocks);
/* We want to call LockRelease just once */
lockOwners[i].nLocks = 1;
locallock->nLocks = 1;
if (!LockRelease(&locallock->tag.lock,
locallock->tag.mode,
owner == NULL))
elog(WARNING, "ReleaseLockForOwner: failed??");
}
break;
}
}
}