aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/storage/lmgr/README16
-rw-r--r--src/backend/storage/lmgr/lock.c198
-rw-r--r--src/backend/storage/lmgr/proc.c10
-rw-r--r--src/include/storage/lock.h4
4 files changed, 139 insertions, 89 deletions
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README
index 87fd312e314..81ad489a5a1 100644
--- a/src/backend/storage/lmgr/README
+++ b/src/backend/storage/lmgr/README
@@ -501,8 +501,8 @@ The caller can then send a cancellation signal. This implements the
principle that autovacuum has a low locking priority (eg it must not block
DDL on the table).
-User Locks
-----------
+User Locks (Advisory Locks)
+---------------------------
User locks are handled totally on the application side as long term
cooperative locks which may extend beyond the normal transaction boundaries.
@@ -516,12 +516,12 @@ level by someone.
User locks and normal locks are completely orthogonal and they don't
interfere with each other.
-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.
+User locks can be acquired either at session level or transaction level.
+A session-level lock request is not automatically released at transaction
+end, but must be explicitly released by the application. (However, any
+remaining locks are always released at session end.) Transaction-level
+user lock requests behave the same as normal lock requests, in that they
+are released at transaction end and do not need explicit unlocking.
Locking during Hot Standby
--------------------------
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 7964415456d..6df7e8b239f 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -256,7 +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 void ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock);
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
PROCLOCK *proclock, LockMethod lockMethodTable);
static void CleanUpLock(LOCK *lock, PROCLOCK *proclock,
@@ -525,11 +525,11 @@ LockAcquireExtended(const LOCKTAG *locktag,
lockMethodTable->lockModeNames[lockmode]);
#endif
- /* Session locks are never transactional, else check table */
- if (!sessionLock && lockMethodTable->transactional)
- owner = CurrentResourceOwner;
- else
+ /* Identify owner for lock */
+ if (sessionLock)
owner = NULL;
+ else
+ owner = CurrentResourceOwner;
/*
* Find or create a LOCALLOCK entry for this lock and lockmode
@@ -1393,11 +1393,11 @@ LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
ResourceOwner owner;
int i;
- /* Session locks are never transactional, else check table */
- if (!sessionLock && lockMethodTable->transactional)
- owner = CurrentResourceOwner;
- else
+ /* Identify owner for lock */
+ if (sessionLock)
owner = NULL;
+ else
+ owner = CurrentResourceOwner;
for (i = locallock->numLockOwners - 1; i >= 0; i--)
{
@@ -1479,31 +1479,6 @@ LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
}
/*
- * 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.
*
@@ -1691,6 +1666,31 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
}
/*
+ * 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;
+
+ ReleaseLockIfHeld(locallock, true);
+ }
+}
+
+/*
* LockReleaseCurrentOwner
* Release all locks belonging to CurrentResourceOwner
*/
@@ -1704,25 +1704,37 @@ LockReleaseCurrentOwner(void)
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
{
- /* Ignore items that must be nontransactional */
- if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
- continue;
-
- ReleaseLockForOwner(locallock, CurrentResourceOwner);
+ ReleaseLockIfHeld(locallock, false);
}
}
/*
- * Subroutine to release a lock belonging to the 'owner' if found.
- * 'owner' can be NULL to release a session lock.
+ * ReleaseLockIfHeld
+ * Release any session-level locks on this lockable object if sessionLock
+ * is true; else, release any locks held by CurrentResourceOwner.
+ *
+ * It is tempting to pass this a ResourceOwner pointer (or NULL for session
+ * locks), but without refactoring LockRelease() we cannot support releasing
+ * locks belonging to resource owners other than CurrentResourceOwner.
+ * If we were to refactor, it'd be a good idea to fix it so we don't have to
+ * do a hashtable lookup of the locallock, too. However, currently this
+ * function isn't used heavily enough to justify refactoring for its
+ * convenience.
*/
static void
-ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner)
+ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock)
{
- int i;
+ ResourceOwner owner;
LOCALLOCKOWNER *lockOwners;
+ int i;
+
+ /* Identify owner for lock (must match LockRelease!) */
+ if (sessionLock)
+ owner = NULL;
+ else
+ owner = CurrentResourceOwner;
- /* Scan to see if there are any locks belonging to the owner */
+ /* Scan to see if there are any locks belonging to the target owner */
lockOwners = locallock->lockOwners;
for (i = locallock->numLockOwners - 1; i >= 0; i--)
{
@@ -1749,8 +1761,8 @@ ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner)
locallock->nLocks = 1;
if (!LockRelease(&locallock->tag.lock,
locallock->tag.mode,
- owner == NULL))
- elog(WARNING, "ReleaseLockForOwner: failed??");
+ sessionLock))
+ elog(WARNING, "ReleaseLockIfHeld: failed??");
}
break;
}
@@ -1780,10 +1792,6 @@ LockReassignCurrentOwner(void)
int ic = -1;
int ip = -1;
- /* Ignore items that must be nontransactional */
- if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
- continue;
-
/*
* Scan to see if there are any locks belonging to current owner or
* its parent
@@ -1944,13 +1952,13 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
* Do the preparatory work for a PREPARE: make 2PC state file records
* for all locks currently held.
*
- * Non-transactional locks are ignored, as are VXID locks.
+ * Session-level locks are ignored, as are VXID locks.
*
- * There are some special cases that we error out on: we can't be holding
- * any session locks (should be OK since only VACUUM uses those) and we
- * can't be holding any locks on temporary objects (since that would mess
- * up the current backend if it tries to exit before the prepared xact is
- * committed).
+ * There are some special cases that we error out on: we can't be holding any
+ * locks at both session and transaction level (since we must either keep or
+ * give away the PROCLOCK object), and we can't be holding any locks on
+ * temporary objects (since that would mess up the current backend if it tries
+ * to exit before the prepared xact is committed).
*/
void
AtPrepare_Locks(void)
@@ -1968,12 +1976,10 @@ AtPrepare_Locks(void)
{
TwoPhaseLockRecord record;
LOCALLOCKOWNER *lockOwners = locallock->lockOwners;
+ bool haveSessionLock;
+ bool haveXactLock;
int i;
- /* Ignore nontransactional locks */
- if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
- continue;
-
/*
* Ignore VXID locks. We don't want those to be held by prepared
* transactions, since they aren't meaningful after a restart.
@@ -1985,14 +1991,38 @@ AtPrepare_Locks(void)
if (locallock->nLocks <= 0)
continue;
- /* Scan to verify there are no session locks */
+ /* Scan to see whether we hold it at session or transaction level */
+ haveSessionLock = haveXactLock = false;
for (i = locallock->numLockOwners - 1; i >= 0; i--)
{
- /* elog not ereport since this should not happen */
if (lockOwners[i].owner == NULL)
- elog(ERROR, "cannot PREPARE when session locks exist");
+ haveSessionLock = true;
+ else
+ haveXactLock = true;
}
+ /* Ignore it if we have only session lock */
+ if (!haveXactLock)
+ continue;
+
+ /*
+ * If we have both session- and transaction-level locks, fail. This
+ * should never happen with regular locks, since we only take those at
+ * session level in some special operations like VACUUM. It's
+ * possible to hit this with advisory locks, though.
+ *
+ * It would be nice if we could keep the session hold and give away
+ * the transactional hold to the prepared xact. However, that would
+ * require two PROCLOCK objects, and we cannot be sure that another
+ * PROCLOCK will be available when it comes time for PostPrepare_Locks
+ * to do the deed. So for now, we error out while we can still do so
+ * safely.
+ */
+ if (haveSessionLock)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot PREPARE while holding both session-level and transaction-level locks on the same object")));
+
/*
* Create a 2PC record.
*/
@@ -2047,6 +2077,11 @@ PostPrepare_Locks(TransactionId xid)
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
{
+ LOCALLOCKOWNER *lockOwners = locallock->lockOwners;
+ bool haveSessionLock;
+ bool haveXactLock;
+ int i;
+
if (locallock->proclock == NULL || locallock->lock == NULL)
{
/*
@@ -2058,15 +2093,29 @@ PostPrepare_Locks(TransactionId xid)
continue;
}
- /* Ignore nontransactional locks */
- if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
- continue;
-
/* Ignore VXID locks */
if (locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
continue;
- /* We already checked there are no session locks */
+ /* Scan to see whether we hold it at session or transaction level */
+ haveSessionLock = haveXactLock = false;
+ for (i = locallock->numLockOwners - 1; i >= 0; i--)
+ {
+ if (lockOwners[i].owner == NULL)
+ haveSessionLock = true;
+ else
+ haveXactLock = true;
+ }
+
+ /* Ignore it if we have only session lock */
+ if (!haveXactLock)
+ continue;
+
+ /* This can't happen, because we already checked it */
+ if (haveSessionLock)
+ ereport(PANIC,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot PREPARE while holding both session-level and transaction-level locks on the same object")));
/* Mark the proclock to show we need to release this lockmode */
if (locallock->nLocks > 0)
@@ -2107,10 +2156,6 @@ PostPrepare_Locks(TransactionId xid)
lock = proclock->tag.myLock;
- /* Ignore nontransactional locks */
- if (!LockMethods[LOCK_LOCKMETHOD(*lock)]->transactional)
- goto next_item;
-
/* Ignore VXID locks */
if (lock->tag.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
goto next_item;
@@ -2122,10 +2167,11 @@ PostPrepare_Locks(TransactionId xid)
Assert(lock->nGranted <= lock->nRequested);
Assert((proclock->holdMask & ~lock->grantMask) == 0);
- /*
- * Since there were no session locks, we should be releasing all
- * locks
- */
+ /* Ignore it if nothing to release (must be a session lock) */
+ if (proclock->releaseMask == 0)
+ goto next_item;
+
+ /* Else we should be releasing all locks */
if (proclock->releaseMask != proclock->holdMask)
elog(PANIC, "we seem to have dropped a bit somewhere");
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 58d95bc0d6b..6ea2fed0b8e 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -642,9 +642,12 @@ LockWaitCancel(void)
* ProcReleaseLocks() -- release locks associated with current transaction
* at main transaction commit or abort
*
- * At main transaction commit, we release all locks except session locks.
+ * At main transaction commit, we release standard locks except session locks.
* At main transaction abort, we release all locks including session locks.
*
+ * Advisory locks are released only if they are transaction-level;
+ * session-level holds remain, whether this is a commit or not.
+ *
* At subtransaction commit, we don't release any locks (so this func is not
* needed at all); we will defer the releasing to the parent transaction.
* At subtransaction abort, we release all locks held by the subtransaction;
@@ -658,10 +661,9 @@ ProcReleaseLocks(bool isCommit)
return;
/* If waiting, get off wait queue (should only be needed after error) */
LockWaitCancel();
- /* Release locks */
+ /* Release standard locks, including session-level if aborting */
LockReleaseAll(DEFAULT_LOCKMETHOD, !isCommit);
-
- /* Release transaction level advisory locks */
+ /* Release transaction-level advisory locks */
LockReleaseAll(USER_LOCKMETHOD, false);
}
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 7ec961f4430..ace485854d7 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -104,6 +104,8 @@ typedef int LOCKMODE;
* are defined in this lock method. Must be less than MAX_LOCKMODES.
*
* transactional -- TRUE if locks are released automatically at xact end.
+ * (Caution: this flag no longer means what you might think, and it
+ * will be removed altogether in 9.2.)
*
* conflictTab -- this is an array of bitmasks showing lock
* mode conflicts. conflictTab[i] is a mask with the j-th bit
@@ -484,8 +486,8 @@ extern LockAcquireResult LockAcquireExtended(const LOCKTAG *locktag,
bool report_memory_error);
extern bool LockRelease(const LOCKTAG *locktag,
LOCKMODE lockmode, bool sessionLock);
-extern void LockReleaseSession(LOCKMETHODID lockmethodid);
extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
+extern void LockReleaseSession(LOCKMETHODID lockmethodid);
extern void LockReleaseCurrentOwner(void);
extern void LockReassignCurrentOwner(void);
extern VirtualTransactionId *GetLockConflicts(const LOCKTAG *locktag,