mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Invent ResourceOwner mechanism as per my recent proposal, and use it to
keep track of portal-related resources separately from transaction-related resources. This allows cursors to work in a somewhat sane fashion with nested transactions. For now, cursor behavior is non-subtransactional, that is a cursor's state does not roll back if you abort a subtransaction that fetched from the cursor. We might want to change that later.
This commit is contained in:
@ -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
|
||||
|
Reference in New Issue
Block a user