mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +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:
@ -505,7 +505,7 @@ User Locks
|
||||
----------
|
||||
|
||||
User locks are handled totally on the application side as long term
|
||||
cooperative locks which extend beyond the normal transaction boundaries.
|
||||
cooperative locks which may extend beyond the normal transaction boundaries.
|
||||
Their purpose is to indicate to an application that someone is `working'
|
||||
on an item. So it is possible to put an user lock on a tuple's oid,
|
||||
retrieve the tuple, work on it for an hour and then update it and remove
|
||||
@ -516,9 +516,12 @@ level by someone.
|
||||
User locks and normal locks are completely orthogonal and they don't
|
||||
interfere with each other.
|
||||
|
||||
User locks are always held as session locks, so that they are not released at
|
||||
transaction end. They must be released explicitly by the application --- but
|
||||
they are released automatically when a backend terminates.
|
||||
There are two types of user locks: session level and transaction level.
|
||||
Session level user locks are not released at transaction end. They must
|
||||
be released explicitly by the application --- but they are released
|
||||
automatically when a backend terminates. On the other hand, transaction
|
||||
level user locks are released automatically at the end of the transaction
|
||||
as like as other normal locks.
|
||||
|
||||
Locking during Hot Standby
|
||||
--------------------------
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -629,8 +629,6 @@ LockWaitCancel(void)
|
||||
* At subtransaction abort, we release all locks held by the subtransaction;
|
||||
* this is implemented by retail releasing of the locks under control of
|
||||
* the ResourceOwner mechanism.
|
||||
*
|
||||
* Note that user locks are not released in any case.
|
||||
*/
|
||||
void
|
||||
ProcReleaseLocks(bool isCommit)
|
||||
@ -641,6 +639,9 @@ ProcReleaseLocks(bool isCommit)
|
||||
LockWaitCancel();
|
||||
/* Release locks */
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, !isCommit);
|
||||
|
||||
/* Release transaction level advisory locks */
|
||||
LockReleaseAll(USER_LOCKMETHOD, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -421,6 +421,23 @@ pg_advisory_lock_int8(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_xact_lock(int8) - acquire xact scoped
|
||||
* exclusive lock on an int8 key
|
||||
*/
|
||||
Datum
|
||||
pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 key = PG_GETARG_INT64(0);
|
||||
LOCKTAG tag;
|
||||
|
||||
SET_LOCKTAG_INT64(tag, key);
|
||||
|
||||
(void) LockAcquire(&tag, ExclusiveLock, false, false);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
|
||||
*/
|
||||
@ -437,6 +454,23 @@ pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_xact_lock_shared(int8) - acquire xact scoped
|
||||
* share lock on an int8 key
|
||||
*/
|
||||
Datum
|
||||
pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 key = PG_GETARG_INT64(0);
|
||||
LOCKTAG tag;
|
||||
|
||||
SET_LOCKTAG_INT64(tag, key);
|
||||
|
||||
(void) LockAcquire(&tag, ShareLock, false, false);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
|
||||
*
|
||||
@ -456,6 +490,26 @@ pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_xact_lock(int8) - acquire xact scoped
|
||||
* exclusive lock on an int8 key, no wait
|
||||
*
|
||||
* Returns true if successful, false if lock not available
|
||||
*/
|
||||
Datum
|
||||
pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 key = PG_GETARG_INT64(0);
|
||||
LOCKTAG tag;
|
||||
LockAcquireResult res;
|
||||
|
||||
SET_LOCKTAG_INT64(tag, key);
|
||||
|
||||
res = LockAcquire(&tag, ExclusiveLock, false, true);
|
||||
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
|
||||
*
|
||||
@ -475,6 +529,26 @@ pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped
|
||||
* share lock on an int8 key, no wait
|
||||
*
|
||||
* Returns true if successful, false if lock not available
|
||||
*/
|
||||
Datum
|
||||
pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 key = PG_GETARG_INT64(0);
|
||||
LOCKTAG tag;
|
||||
LockAcquireResult res;
|
||||
|
||||
SET_LOCKTAG_INT64(tag, key);
|
||||
|
||||
res = LockAcquire(&tag, ShareLock, false, true);
|
||||
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_unlock(int8) - release exclusive lock on an int8 key
|
||||
*
|
||||
@ -530,6 +604,24 @@ pg_advisory_lock_int4(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_xact_lock(int4, int4) - acquire xact scoped
|
||||
* exclusive lock on 2 int4 keys
|
||||
*/
|
||||
Datum
|
||||
pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 key1 = PG_GETARG_INT32(0);
|
||||
int32 key2 = PG_GETARG_INT32(1);
|
||||
LOCKTAG tag;
|
||||
|
||||
SET_LOCKTAG_INT32(tag, key1, key2);
|
||||
|
||||
(void) LockAcquire(&tag, ExclusiveLock, false, false);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
|
||||
*/
|
||||
@ -547,6 +639,24 @@ pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
|
||||
* share lock on 2 int4 keys
|
||||
*/
|
||||
Datum
|
||||
pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 key1 = PG_GETARG_INT32(0);
|
||||
int32 key2 = PG_GETARG_INT32(1);
|
||||
LOCKTAG tag;
|
||||
|
||||
SET_LOCKTAG_INT32(tag, key1, key2);
|
||||
|
||||
(void) LockAcquire(&tag, ShareLock, false, false);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
|
||||
*
|
||||
@ -567,6 +677,27 @@ pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped
|
||||
* exclusive lock on 2 int4 keys, no wait
|
||||
*
|
||||
* Returns true if successful, false if lock not available
|
||||
*/
|
||||
Datum
|
||||
pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 key1 = PG_GETARG_INT32(0);
|
||||
int32 key2 = PG_GETARG_INT32(1);
|
||||
LOCKTAG tag;
|
||||
LockAcquireResult res;
|
||||
|
||||
SET_LOCKTAG_INT32(tag, key1, key2);
|
||||
|
||||
res = LockAcquire(&tag, ExclusiveLock, false, true);
|
||||
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
|
||||
*
|
||||
@ -587,6 +718,27 @@ pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
|
||||
* share lock on 2 int4 keys, no wait
|
||||
*
|
||||
* Returns true if successful, false if lock not available
|
||||
*/
|
||||
Datum
|
||||
pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 key1 = PG_GETARG_INT32(0);
|
||||
int32 key2 = PG_GETARG_INT32(1);
|
||||
LOCKTAG tag;
|
||||
LockAcquireResult res;
|
||||
|
||||
SET_LOCKTAG_INT32(tag, key1, key2);
|
||||
|
||||
res = LockAcquire(&tag, ShareLock, false, true);
|
||||
|
||||
PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
|
||||
*
|
||||
@ -633,7 +785,7 @@ pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
pg_advisory_unlock_all(PG_FUNCTION_ARGS)
|
||||
{
|
||||
LockReleaseAll(USER_LOCKMETHOD, true);
|
||||
LockReleaseSession(USER_LOCKMETHOD);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
Reference in New Issue
Block a user