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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user