summaryrefslogtreecommitdiff
path: root/src/backend/storage/lmgr/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/lmgr/proc.c')
-rw-r--r--src/backend/storage/lmgr/proc.c144
1 files changed, 67 insertions, 77 deletions
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index ed270b6f1bd..55765cb2507 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -14,7 +14,7 @@
*/
/*
* Interface (a):
- * ProcSleep(), ProcWakeup(),
+ * JoinWaitQueue(), ProcSleep(), ProcWakeup()
*
* Waiting for a lock causes the backend to be put to sleep. Whoever releases
* the lock wakes the process up again (and gives it an error code so it knows
@@ -80,9 +80,6 @@ PROC_HDR *ProcGlobal = NULL;
NON_EXEC_STATIC PGPROC *AuxiliaryProcs = NULL;
PGPROC *PreparedXactProcs = NULL;
-/* If we are waiting for a lock, this points to the associated LOCALLOCK */
-static LOCALLOCK *lockAwaited = NULL;
-
static DeadLockState deadlock_state = DS_NOT_YET_CHECKED;
/* Is a deadlock check pending? */
@@ -754,18 +751,6 @@ HaveNFreeProcs(int n, int *nfree)
}
/*
- * Check if the current process is awaiting a lock.
- */
-bool
-IsWaitingForLock(void)
-{
- if (lockAwaited == NULL)
- return false;
-
- return true;
-}
-
-/*
* Cancel any pending wait for lock, when aborting a transaction, and revert
* any strong lock count acquisition for a lock being acquired.
*
@@ -776,6 +761,7 @@ IsWaitingForLock(void)
void
LockErrorCleanup(void)
{
+ LOCALLOCK *lockAwaited;
LWLock *partitionLock;
DisableTimeoutParams timeouts[2];
@@ -784,6 +770,7 @@ LockErrorCleanup(void)
AbortStrongLockAcquire();
/* Nothing to do if we weren't waiting for a lock */
+ lockAwaited = GetAwaitedLock();
if (lockAwaited == NULL)
{
RESUME_INTERRUPTS();
@@ -825,8 +812,6 @@ LockErrorCleanup(void)
GrantAwaitedLock();
}
- lockAwaited = NULL;
-
LWLockRelease(partitionLock);
RESUME_INTERRUPTS();
@@ -1078,7 +1063,7 @@ AuxiliaryPidGetProc(int pid)
/*
- * ProcSleep -- put a process to sleep on the specified lock
+ * JoinWaitQueue -- join the wait queue on the specified lock
*
* It's not actually guaranteed that we need to wait when this function is
* called, because it could be that when we try to find a position at which
@@ -1087,37 +1072,44 @@ AuxiliaryPidGetProc(int pid)
* we get the lock immediately. Because of this, it's sensible for this function
* to have a dontWait argument, despite the name.
*
- * The lock table's partition lock must be held at entry, and will be held
- * at exit.
+ * On entry, the caller has already set up LOCK and PROCLOCK entries to
+ * reflect that we have "requested" the lock. The caller is responsible for
+ * cleaning that up, if we end up not joining the queue after all.
+ *
+ * The lock table's partition lock must be held at entry, and is still held
+ * at exit. The caller must release it before calling ProcSleep().
*
- * Result: PROC_WAIT_STATUS_OK if we acquired the lock, PROC_WAIT_STATUS_ERROR
- * if not (if dontWait = true, we would have had to wait; if dontWait = false,
- * this is a deadlock).
+ * Result is one of the following:
*
- * ASSUME: that no one will fiddle with the queue until after
- * we release the partition lock.
+ * PROC_WAIT_STATUS_OK - lock was immediately granted
+ * PROC_WAIT_STATUS_WAITING - joined the wait queue; call ProcSleep()
+ * PROC_WAIT_STATUS_ERROR - immediate deadlock was detected, or would
+ * need to wait and dontWait == true
*
* NOTES: The process queue is now a priority queue for locking.
*/
ProcWaitStatus
-ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait)
+JoinWaitQueue(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait)
{
LOCKMODE lockmode = locallock->tag.mode;
LOCK *lock = locallock->lock;
PROCLOCK *proclock = locallock->proclock;
uint32 hashcode = locallock->hashcode;
- LWLock *partitionLock = LockHashPartitionLock(hashcode);
+ LWLock *partitionLock PG_USED_FOR_ASSERTS_ONLY = LockHashPartitionLock(hashcode);
dclist_head *waitQueue = &lock->waitProcs;
PGPROC *insert_before = NULL;
LOCKMASK myProcHeldLocks;
LOCKMASK myHeldLocks;
- TimestampTz standbyWaitStart = 0;
bool early_deadlock = false;
- bool allow_autovacuum_cancel = true;
- bool logged_recovery_conflict = false;
- ProcWaitStatus myWaitStatus;
PGPROC *leader = MyProc->lockGroupLeader;
+ Assert(LWLockHeldByMeInMode(partitionLock, LW_EXCLUSIVE));
+
+ /*
+ * Set bitmask of locks this process already holds on this object.
+ */
+ myHeldLocks = MyProc->heldLocks = proclock->holdMask;
+
/*
* Determine which locks we're already holding.
*
@@ -1205,7 +1197,6 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait)
{
/* Skip the wait and just grant myself the lock. */
GrantLock(lock, proclock, lockmode);
- GrantAwaitedLock();
return PROC_WAIT_STATUS_OK;
}
@@ -1219,6 +1210,13 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait)
}
/*
+ * If we detected deadlock, give up without waiting. This must agree with
+ * CheckDeadLock's recovery code.
+ */
+ if (early_deadlock)
+ return PROC_WAIT_STATUS_ERROR;
+
+ /*
* At this point we know that we'd really need to sleep. If we've been
* commanded not to do that, bail out.
*/
@@ -1243,34 +1241,43 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait)
MyProc->waitStatus = PROC_WAIT_STATUS_WAITING;
- /*
- * If we detected deadlock, give up without waiting. This must agree with
- * CheckDeadLock's recovery code.
- */
- if (early_deadlock)
- {
- RemoveFromWaitQueue(MyProc, hashcode);
- return PROC_WAIT_STATUS_ERROR;
- }
+ return PROC_WAIT_STATUS_WAITING;
+}
- /* mark that we are waiting for a lock */
- lockAwaited = locallock;
+/*
+ * ProcSleep -- put process to sleep waiting on lock
+ *
+ * This must be called when JoinWaitQueue() returns PROC_WAIT_STATUS_WAITING.
+ * Returns after the lock has been granted, or if a deadlock is detected. Can
+ * also bail out with ereport(ERROR), if some other error condition, or a
+ * timeout or cancellation is triggered.
+ *
+ * Result is one of the following:
+ *
+ * PROC_WAIT_STATUS_OK - lock was granted
+ * PROC_WAIT_STATUS_ERROR - a deadlock was detected
+ */
+ProcWaitStatus
+ProcSleep(LOCALLOCK *locallock)
+{
+ LOCKMODE lockmode = locallock->tag.mode;
+ LOCK *lock = locallock->lock;
+ uint32 hashcode = locallock->hashcode;
+ LWLock *partitionLock = LockHashPartitionLock(hashcode);
+ TimestampTz standbyWaitStart = 0;
+ bool allow_autovacuum_cancel = true;
+ bool logged_recovery_conflict = false;
+ ProcWaitStatus myWaitStatus;
- /*
- * Release the lock table's partition lock.
- *
- * NOTE: this may also cause us to exit critical-section state, possibly
- * allowing a cancel/die interrupt to be accepted. This is OK because we
- * have recorded the fact that we are waiting for a lock, and so
- * LockErrorCleanup will clean up if cancel/die happens.
- */
- LWLockRelease(partitionLock);
+ /* The caller must've armed the on-error cleanup mechanism */
+ Assert(GetAwaitedLock() == locallock);
+ Assert(!LWLockHeldByMe(partitionLock));
/*
- * Also, now that we will successfully clean up after an ereport, it's
- * safe to check to see if there's a buffer pin deadlock against the
- * Startup process. Of course, that's only necessary if we're doing Hot
- * Standby and are not the Startup process ourselves.
+ * Now that we will successfully clean up after an ereport, it's safe to
+ * check to see if there's a buffer pin deadlock against the Startup
+ * process. Of course, that's only necessary if we're doing Hot Standby
+ * and are not the Startup process ourselves.
*/
if (RecoveryInProgress() && !InRecovery)
CheckRecoveryConflictDeadlock();
@@ -1680,28 +1687,11 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait)
NULL, false);
/*
- * Re-acquire the lock table's partition lock. We have to do this to hold
- * off cancel/die interrupts before we can mess with lockAwaited (else we
- * might have a missed or duplicated locallock update).
- */
- LWLockAcquire(partitionLock, LW_EXCLUSIVE);
-
- /*
- * We no longer want LockErrorCleanup to do anything.
- */
- lockAwaited = NULL;
-
- /*
- * If we got the lock, be sure to remember it in the locallock table.
- */
- if (MyProc->waitStatus == PROC_WAIT_STATUS_OK)
- GrantAwaitedLock();
-
- /*
* We don't have to do anything else, because the awaker did all the
- * necessary update of the lock table and MyProc.
+ * necessary updates of the lock table and MyProc. (The caller is
+ * responsible for updating the local lock table.)
*/
- return MyProc->waitStatus;
+ return myWaitStatus;
}