aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/lmgr/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/lmgr/lock.c')
-rw-r--r--src/backend/storage/lmgr/lock.c216
1 files changed, 112 insertions, 104 deletions
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index cee3f03c4b0..edc5020c6ae 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -409,8 +409,7 @@ static PROCLOCK *SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
static void BeginStrongLockAcquire(LOCALLOCK *locallock, uint32 fasthashcode);
static void FinishStrongLockAcquire(void);
-static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner,
- bool dontWait);
+static ProcWaitStatus WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
static void ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock);
static void LockReassignOwner(LOCALLOCK *locallock, ResourceOwner parent);
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
@@ -843,6 +842,7 @@ LockAcquireExtended(const LOCKTAG *locktag,
uint32 hashcode;
LWLock *partitionLock;
bool found_conflict;
+ ProcWaitStatus waitResult;
bool log_lock = false;
if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
@@ -1091,16 +1091,83 @@ LockAcquireExtended(const LOCKTAG *locktag,
{
/* No conflict with held or previously requested locks */
GrantLock(lock, proclock, lockmode);
- GrantLockLocal(locallock, owner);
+ waitResult = PROC_WAIT_STATUS_OK;
}
else
{
/*
- * Sleep till someone wakes me up. We do this even in the dontWait
- * case, because while trying to go to sleep, we may discover that we
- * can acquire the lock immediately after all.
+ * Join the lock's wait queue. We call this even in the dontWait
+ * case, because JoinWaitQueue() may discover that we can acquire the
+ * lock immediately after all.
+ */
+ waitResult = JoinWaitQueue(locallock, lockMethodTable, dontWait);
+ }
+
+ if (waitResult == PROC_WAIT_STATUS_ERROR)
+ {
+ /*
+ * We're not getting the lock because a deadlock was detected already
+ * while trying to join the wait queue, or because we would have to
+ * wait but the caller requested no blocking.
+ *
+ * Undo the changes to shared entries before releasing the partition
+ * lock.
*/
- WaitOnLock(locallock, owner, dontWait);
+ AbortStrongLockAcquire();
+
+ if (proclock->holdMask == 0)
+ {
+ uint32 proclock_hashcode;
+
+ proclock_hashcode = ProcLockHashCode(&proclock->tag,
+ hashcode);
+ dlist_delete(&proclock->lockLink);
+ dlist_delete(&proclock->procLink);
+ if (!hash_search_with_hash_value(LockMethodProcLockHash,
+ &(proclock->tag),
+ proclock_hashcode,
+ HASH_REMOVE,
+ NULL))
+ elog(PANIC, "proclock table corrupted");
+ }
+ else
+ PROCLOCK_PRINT("LockAcquire: did not join wait queue", proclock);
+ lock->nRequested--;
+ lock->requested[lockmode]--;
+ LOCK_PRINT("LockAcquire: did not join wait queue",
+ lock, lockmode);
+ Assert((lock->nRequested > 0) &&
+ (lock->requested[lockmode] >= 0));
+ Assert(lock->nGranted <= lock->nRequested);
+ LWLockRelease(partitionLock);
+ if (locallock->nLocks == 0)
+ RemoveLocalLock(locallock);
+
+ if (dontWait)
+ {
+ if (locallockp)
+ *locallockp = NULL;
+ return LOCKACQUIRE_NOT_AVAIL;
+ }
+ else
+ {
+ DeadLockReport();
+ /* DeadLockReport() will not return */
+ }
+ }
+
+ /*
+ * We are now in the lock queue, or the lock was already granted. If
+ * queued, go to sleep.
+ */
+ if (waitResult == PROC_WAIT_STATUS_WAITING)
+ {
+ Assert(!dontWait);
+ PROCLOCK_PRINT("LockAcquire: sleeping on lock", proclock);
+ LOCK_PRINT("LockAcquire: sleeping on lock", lock, lockmode);
+ LWLockRelease(partitionLock);
+
+ waitResult = WaitOnLock(locallock, owner);
/*
* NOTE: do not do any material change of state between here and
@@ -1108,68 +1175,24 @@ LockAcquireExtended(const LOCKTAG *locktag,
* done when the lock was granted to us --- see notes in WaitOnLock.
*/
- /*
- * Check the proclock entry status. If dontWait = true, this is an
- * expected case; otherwise, it will only happen if something in the
- * ipc communication doesn't work correctly.
- */
- if (!(proclock->holdMask & LOCKBIT_ON(lockmode)))
+ if (waitResult == PROC_WAIT_STATUS_ERROR)
{
- AbortStrongLockAcquire();
-
- if (dontWait)
- {
- /*
- * We can't acquire the lock immediately. If caller specified
- * no blocking, remove useless table entries and return
- * LOCKACQUIRE_NOT_AVAIL without waiting.
- */
- if (proclock->holdMask == 0)
- {
- uint32 proclock_hashcode;
-
- proclock_hashcode = ProcLockHashCode(&proclock->tag,
- hashcode);
- dlist_delete(&proclock->lockLink);
- dlist_delete(&proclock->procLink);
- if (!hash_search_with_hash_value(LockMethodProcLockHash,
- &(proclock->tag),
- proclock_hashcode,
- HASH_REMOVE,
- NULL))
- elog(PANIC, "proclock table corrupted");
- }
- else
- PROCLOCK_PRINT("LockAcquire: NOWAIT", proclock);
- lock->nRequested--;
- lock->requested[lockmode]--;
- LOCK_PRINT("LockAcquire: conditional lock failed",
- lock, lockmode);
- Assert((lock->nRequested > 0) &&
- (lock->requested[lockmode] >= 0));
- Assert(lock->nGranted <= lock->nRequested);
- LWLockRelease(partitionLock);
- if (locallock->nLocks == 0)
- RemoveLocalLock(locallock);
- if (locallockp)
- *locallockp = NULL;
- return LOCKACQUIRE_NOT_AVAIL;
- }
- else
- {
- /*
- * We should have gotten the lock, but somehow that didn't
- * happen. If we get here, it's a bug.
- */
- PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock);
- LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode);
- LWLockRelease(partitionLock);
- elog(ERROR, "LockAcquire failed");
- }
+ /*
+ * We failed as a result of a deadlock, see CheckDeadLock(). Quit
+ * now.
+ */
+ Assert(!dontWait);
+ DeadLockReport();
+ /* DeadLockReport() will not return */
}
- PROCLOCK_PRINT("LockAcquire: granted", proclock);
- LOCK_PRINT("LockAcquire: granted", lock, lockmode);
}
+ else
+ LWLockRelease(partitionLock);
+ Assert(waitResult == PROC_WAIT_STATUS_OK);
+
+ /* The lock was granted to us. Update the local lock entry accordingly */
+ Assert((proclock->holdMask & LOCKBIT_ON(lockmode)) != 0);
+ GrantLockLocal(locallock, owner);
/*
* Lock state is fully up-to-date now; if we error out after this, no
@@ -1177,8 +1200,6 @@ LockAcquireExtended(const LOCKTAG *locktag,
*/
FinishStrongLockAcquire();
- LWLockRelease(partitionLock);
-
/*
* Emit a WAL record if acquisition of this lock needs to be replayed in a
* standby server.
@@ -1820,6 +1841,15 @@ GrantAwaitedLock(void)
}
/*
+ * GetAwaitedLock -- Return the lock we're currently doing WaitOnLock on.
+ */
+LOCALLOCK *
+GetAwaitedLock(void)
+{
+ return awaitedLock;
+}
+
+/*
* MarkLockClear -- mark an acquired lock as "clear"
*
* This means that we know we have absorbed all sinval messages that other
@@ -1836,14 +1866,12 @@ MarkLockClear(LOCALLOCK *locallock)
/*
* WaitOnLock -- wait to acquire a lock
*
- * The appropriate partition lock must be held at entry, and will still be
- * held at exit.
+ * This is a wrapper around ProcSleep, with extra tracing and bookkeeping.
*/
-static void
-WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner, bool dontWait)
+static ProcWaitStatus
+WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
{
- LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
- LockMethod lockMethodTable = LockMethods[lockmethodid];
+ ProcWaitStatus result;
TRACE_POSTGRESQL_LOCK_WAIT_START(locallock->tag.lock.locktag_field1,
locallock->tag.lock.locktag_field2,
@@ -1852,12 +1880,13 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner, bool dontWait)
locallock->tag.lock.locktag_type,
locallock->tag.mode);
- LOCK_PRINT("WaitOnLock: sleeping on lock",
- locallock->lock, locallock->tag.mode);
-
/* adjust the process title to indicate that it's waiting */
set_ps_display_suffix("waiting");
+ /*
+ * Record the fact that we are waiting for a lock, so that
+ * LockErrorCleanup will clean up if cancel/die happens.
+ */
awaitedLock = locallock;
awaitedOwner = owner;
@@ -1880,30 +1909,7 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner, bool dontWait)
*/
PG_TRY();
{
- /*
- * If dontWait = true, we handle success and failure in the same way
- * here. The caller will be able to sort out what has happened.
- */
- if (ProcSleep(locallock, lockMethodTable, dontWait) != PROC_WAIT_STATUS_OK
- && !dontWait)
- {
-
- /*
- * We failed as a result of a deadlock, see CheckDeadLock(). Quit
- * now.
- */
- awaitedLock = NULL;
- LOCK_PRINT("WaitOnLock: aborting on lock",
- locallock->lock, locallock->tag.mode);
- LWLockRelease(LockHashPartitionLock(locallock->hashcode));
-
- /*
- * Now that we aren't holding the partition lock, we can give an
- * error report including details about the detected deadlock.
- */
- DeadLockReport();
- /* not reached */
- }
+ result = ProcSleep(locallock);
}
PG_CATCH();
{
@@ -1917,20 +1923,22 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner, bool dontWait)
}
PG_END_TRY();
+ /*
+ * We no longer want LockErrorCleanup to do anything.
+ */
awaitedLock = NULL;
/* reset ps display to remove the suffix */
set_ps_display_remove_suffix();
- LOCK_PRINT("WaitOnLock: wakeup on lock",
- locallock->lock, locallock->tag.mode);
-
TRACE_POSTGRESQL_LOCK_WAIT_DONE(locallock->tag.lock.locktag_field1,
locallock->tag.lock.locktag_field2,
locallock->tag.lock.locktag_field3,
locallock->tag.lock.locktag_field4,
locallock->tag.lock.locktag_type,
locallock->tag.mode);
+
+ return result;
}
/*