aboutsummaryrefslogtreecommitdiff
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.c110
1 files changed, 78 insertions, 32 deletions
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index abe44e808ad..3c7249ffc13 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.149 2004/07/01 00:50:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.150 2004/07/17 03:28:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,7 +40,6 @@
*/
#include "postgres.h"
-#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
@@ -51,6 +50,8 @@
#include "storage/proc.h"
#include "storage/sinval.h"
#include "storage/spin.h"
+#include "utils/resowner.h"
+
/* GUC variables */
int DeadlockTimeout = 1000;
@@ -75,6 +76,11 @@ static PGPROC *DummyProcs = NULL;
static bool waitingForLock = false;
static bool waitingForSignal = false;
+/* Auxiliary state, valid when waitingForLock is true */
+static LOCKTAG waitingForLockTag;
+static TransactionId waitingForLockXid;
+static LOCKMODE waitingForLockMode;
+
/* Mark these volatile because they can be changed by signal handler */
static volatile bool statement_timeout_active = false;
static volatile bool deadlock_timeout_active = false;
@@ -234,7 +240,7 @@ InitProcess(void)
* prepared for us by InitProcGlobal.
*/
SHMQueueElemInit(&(MyProc->links));
- MyProc->errType = STATUS_OK;
+ MyProc->waitStatus = STATUS_OK;
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
MyProc->pid = MyProcPid;
@@ -308,7 +314,7 @@ InitDummyProcess(int proctype)
*/
MyProc->pid = MyProcPid; /* marks dummy proc as in use by me */
SHMQueueElemInit(&(MyProc->links));
- MyProc->errType = STATUS_OK;
+ MyProc->waitStatus = STATUS_OK;
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
MyProc->databaseId = MyDatabaseId;
@@ -348,15 +354,40 @@ LockWaitCancel(void)
if (!waitingForLock)
return false;
- waitingForLock = false;
-
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
disable_sig_alarm(false);
/* Unlink myself from the wait queue, if on it (might not be anymore!) */
LWLockAcquire(LockMgrLock, LW_EXCLUSIVE);
+
if (MyProc->links.next != INVALID_OFFSET)
+ {
+ /* We could not have been granted the lock yet */
+ Assert(MyProc->waitStatus == STATUS_ERROR);
RemoveFromWaitQueue(MyProc);
+ }
+ else
+ {
+ /*
+ * Somebody kicked us off the lock queue already. Perhaps they
+ * granted us the lock, or perhaps they detected a deadlock.
+ * If they did grant us the lock, we'd better remember it in
+ * CurrentResourceOwner.
+ *
+ * Exception: if CurrentResourceOwner is NULL then we can't do
+ * anything. This could only happen when we are invoked from ProcKill
+ * or some similar place, where all our locks are about to be released
+ * anyway.
+ */
+ if (MyProc->waitStatus == STATUS_OK && CurrentResourceOwner != NULL)
+ ResourceOwnerRememberLock(CurrentResourceOwner,
+ &waitingForLockTag,
+ waitingForLockXid,
+ waitingForLockMode);
+ }
+
+ waitingForLock = false;
+
LWLockRelease(LockMgrLock);
/*
@@ -380,34 +411,29 @@ LockWaitCancel(void)
/*
* ProcReleaseLocks() -- release locks associated with current transaction
- * at main transaction and subtransaction commit or abort
- *
- * The options for which locks to release are the same as for the underlying
- * LockReleaseAll() function.
- *
- * Notes:
+ * at main transaction commit or abort
*
* At main transaction commit, we release all locks except session locks.
* At main transaction abort, we release all locks including session locks;
* this lets us clean up after a VACUUM FULL failure.
*
* At subtransaction commit, we don't release any locks (so this func is not
- * called at all); we will defer the releasing to the parent transaction.
+ * needed at all); we will defer the releasing to the parent transaction.
* At subtransaction abort, we release all locks held by the subtransaction;
- * this is implemented by passing in the Xids of the failed subxact and its
- * children in the xids[] array.
+ * 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(LockReleaseWhich which, int nxids, TransactionId *xids)
+ProcReleaseLocks(bool isCommit)
{
if (!MyProc)
return;
/* If waiting, get off wait queue (should only be needed after error) */
LockWaitCancel();
/* Release locks */
- LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, which, nxids, xids);
+ LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, !isCommit);
}
@@ -440,11 +466,11 @@ ProcKill(int code, Datum arg)
LockWaitCancel();
/* Remove from the standard lock table */
- LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, ReleaseAll, 0, NULL);
+ LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, true);
#ifdef USER_LOCKS
/* Remove from the user lock table */
- LockReleaseAll(USER_LOCKMETHOD, MyProc, ReleaseAll, 0, NULL);
+ LockReleaseAll(USER_LOCKMETHOD, MyProc, true);
#endif
SpinLockAcquire(ProcStructLock);
@@ -618,6 +644,10 @@ ProcSleep(LockMethod lockMethodTable,
{
/* Skip the wait and just grant myself the lock. */
GrantLock(lock, proclock, lockmode);
+ ResourceOwnerRememberLock(CurrentResourceOwner,
+ &lock->tag,
+ proclock->tag.xid,
+ lockmode);
return STATUS_OK;
}
/* Break out of loop to put myself before him */
@@ -653,7 +683,7 @@ ProcSleep(LockMethod lockMethodTable,
MyProc->waitHolder = proclock;
MyProc->waitLockMode = lockmode;
- MyProc->errType = STATUS_OK; /* initialize result for success */
+ MyProc->waitStatus = STATUS_ERROR; /* initialize result for error */
/*
* If we detected deadlock, give up without waiting. This must agree
@@ -663,11 +693,13 @@ ProcSleep(LockMethod lockMethodTable,
if (early_deadlock)
{
RemoveFromWaitQueue(MyProc);
- MyProc->errType = STATUS_ERROR;
return STATUS_ERROR;
}
/* mark that we are waiting for a lock */
+ waitingForLockTag = lock->tag;
+ waitingForLockXid = proclock->tag.xid;
+ waitingForLockMode = lockmode;
waitingForLock = true;
/*
@@ -683,7 +715,7 @@ ProcSleep(LockMethod lockMethodTable,
/*
* Set timer so we can wake up after awhile and check for a deadlock.
* If a deadlock is detected, the handler releases the process's
- * semaphore and sets MyProc->errType = STATUS_ERROR, allowing us to
+ * semaphore and sets MyProc->waitStatus = STATUS_ERROR, allowing us to
* know that we must report failure rather than success.
*
* By delaying the check until we've waited for a bit, we can avoid
@@ -703,8 +735,10 @@ ProcSleep(LockMethod lockMethodTable,
* We pass interruptOK = true, which eliminates a window in which
* cancel/die interrupts would be held off undesirably. This is a
* promise that we don't mind losing control to a cancel/die interrupt
- * here. We don't, because we have no state-change work to do after
- * being granted the lock (the grantor did it all).
+ * here. We don't, because we have no shared-state-change work to do
+ * after being granted the lock (the grantor did it all). We do have
+ * to worry about updating the local CurrentResourceOwner, but if we
+ * lose control to an error, LockWaitCancel will fix that up.
*/
PGSemaphoreLock(&MyProc->sem, true);
@@ -715,20 +749,32 @@ ProcSleep(LockMethod lockMethodTable,
elog(FATAL, "could not disable timer for process wakeup");
/*
- * Now there is nothing for LockWaitCancel to do.
+ * Re-acquire the locktable's masterLock. We have to do this to hold
+ * off cancel/die interrupts before we can mess with waitingForLock
+ * (else we might have a missed or duplicated CurrentResourceOwner
+ * update).
+ */
+ LWLockAcquire(masterLock, LW_EXCLUSIVE);
+
+ /*
+ * We no longer want LockWaitCancel to do anything.
*/
waitingForLock = false;
/*
- * Re-acquire the locktable's masterLock.
+ * If we got the lock, be sure to remember it in CurrentResourceOwner.
*/
- LWLockAcquire(masterLock, LW_EXCLUSIVE);
+ if (MyProc->waitStatus == STATUS_OK)
+ ResourceOwnerRememberLock(CurrentResourceOwner,
+ &lock->tag,
+ proclock->tag.xid,
+ lockmode);
/*
* We don't have to do anything else, because the awaker did all the
* necessary update of the lock table and MyProc.
*/
- return MyProc->errType;
+ return MyProc->waitStatus;
}
@@ -743,7 +789,7 @@ ProcSleep(LockMethod lockMethodTable,
* to twiddle the lock's request counts too --- see RemoveFromWaitQueue.
*/
PGPROC *
-ProcWakeup(PGPROC *proc, int errType)
+ProcWakeup(PGPROC *proc, int waitStatus)
{
PGPROC *retProc;
@@ -764,7 +810,7 @@ ProcWakeup(PGPROC *proc, int errType)
/* Clean up process' state and pass it the ok/fail signal */
proc->waitLock = NULL;
proc->waitHolder = NULL;
- proc->errType = errType;
+ proc->waitStatus = waitStatus;
/* And awaken it */
PGSemaphoreUnlock(&proc->sem);
@@ -891,10 +937,10 @@ CheckDeadLock(void)
RemoveFromWaitQueue(MyProc);
/*
- * Set MyProc->errType to STATUS_ERROR so that ProcSleep will report
+ * Set MyProc->waitStatus to STATUS_ERROR so that ProcSleep will report
* an error after we return from the signal handler.
*/
- MyProc->errType = STATUS_ERROR;
+ MyProc->waitStatus = STATUS_ERROR;
/*
* Unlock my semaphore so that the interrupted ProcSleep() call can