*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.73 2001/01/12 21:53:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.74 2001/01/14 05:08:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "access/nbtree.h"
+#include "miscadmin.h"
typedef struct
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.92 2001/01/12 21:53:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.93 2001/01/14 05:08:14 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
if (s->state != TRANS_INPROGRESS)
elog(NOTICE, "CommitTransaction and not in in-progress state ");
+ /* Prevent cancel/die interrupt while cleaning up */
+ START_CRIT_SECTION();
+
/* ----------------
* Tell the trigger manager that this transaction is about to be
* committed. He'll invoke all trigger deferred until XACT before
* ----------------
*/
s->state = TRANS_DEFAULT;
+
+ END_CRIT_SECTION();
}
/* --------------------------------
{
TransactionState s = CurrentTransactionState;
+ /* Prevent cancel/die interrupt while cleaning up */
+ START_CRIT_SECTION();
+
/*
* Let others to know about no transaction in progress - vadim
* 11/26/96
*/
ProcReleaseSpins(NULL);
UnlockBuffers();
+ /*
+ * Also clean up any open wait for lock, since the lock manager
+ * will choke if we try to wait for another lock before doing this.
+ */
+ LockWaitCancel();
/* ----------------
* check the current transaction state
* ----------------
*/
if (s->state == TRANS_DISABLED)
+ {
+ END_CRIT_SECTION();
return;
+ }
if (s->state != TRANS_INPROGRESS)
elog(NOTICE, "AbortTransaction and not in in-progress state");
* State remains TRANS_ABORT until CleanupTransaction().
* ----------------
*/
+ END_CRIT_SECTION();
}
/* --------------------------------
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.49 2001/01/12 21:53:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.50 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int XLOGbuffers = 8;
int XLOGfiles = 0; /* how many files to pre-allocate */
XLogRecPtr MyLastRecPtr = {0, 0};
-volatile uint32 CritSectionCount = 0;
bool InRecovery = false;
StartUpID ThisStartUpID = 0;
XLogRecPtr RedoRecPtr;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.102 2000/12/28 13:00:12 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.103 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (!IsUnderPostmaster)
{
- pqsignal(SIGINT, (pqsigfunc) die);
- pqsignal(SIGHUP, (pqsigfunc) die);
- pqsignal(SIGTERM, (pqsigfunc) die);
+ pqsignal(SIGHUP, die);
+ pqsignal(SIGINT, die);
+ pqsignal(SIGTERM, die);
+ pqsignal(SIGQUIT, die);
}
/*
* abort processing resumes here
* ----------------
*/
- pqsignal(SIGHUP, handle_warn);
-
if (sigsetjmp(Warn_restart, 1) != 0)
{
Warnings++;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.10 2000/12/02 19:38:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.11 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Check for user-requested abort. Note we want this to be inside a
* transaction, so xact.c doesn't issue useless NOTICE.
*/
- if (QueryCancel)
- CancelQuery();
+ CHECK_FOR_INTERRUPTS();
/*
* Race condition -- if the pg_class tuple has gone away since the
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.128 2001/01/06 03:33:17 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.129 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
bool need_delim = false;
- if (QueryCancel)
- CancelQuery();
+ CHECK_FOR_INTERRUPTS();
if (binary)
{
while (!done)
{
- if (QueryCancel)
- {
- lineno = 0;
- CancelQuery();
- }
+ CHECK_FOR_INTERRUPTS();
lineno++;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.182 2001/01/12 21:53:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.183 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Check for user-requested abort. Note we want this to be inside a
* transaction, so xact.c doesn't issue useless NOTICE.
*/
- if (QueryCancel)
- CancelQuery();
+ CHECK_FOR_INTERRUPTS();
/*
* Race condition -- if the pg_class tuple has gone away since the
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.22 2000/10/26 21:35:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.23 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
TupleTableSlot *result;
+ CHECK_FOR_INTERRUPTS();
+
/* ----------------
* deal with NULL nodes..
* ----------------
*/
-
- if (QueryCancel)
- CancelQuery();
-
if (node == NULL)
return NULL;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.103 2001/01/12 21:53:57 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.104 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool bufferLockHeld);
static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum,
bool *foundPtr, bool bufferLockHeld);
+static int ReleaseBufferWithBufferLock(Buffer buffer);
static int BufferReplace(BufferDesc *bufHdr);
void PrintBufferDescs(void);
{
bufHdr = &BufferDescriptors[buffer - 1];
Assert(PrivateRefCount[buffer - 1] > 0);
- PrivateRefCount[buffer - 1]--;
- if (PrivateRefCount[buffer - 1] == 0)
+ if (PrivateRefCount[buffer - 1] > 1)
+ {
+ PrivateRefCount[buffer - 1]--;
+ }
+ else
{
SpinAcquire(BufMgrLock);
+ PrivateRefCount[buffer - 1] = 0;
Assert(bufHdr->refcount > 0);
bufHdr->refcount--;
if (bufHdr->refcount == 0)
/* Assert checks that buffer will actually get freed! */
Assert(PrivateRefCount[i - 1] == 1 &&
bufHdr->refcount == 1);
- /* ReleaseBuffer expects we do not hold the lock at entry */
- SpinRelease(BufMgrLock);
- ReleaseBuffer(i);
- SpinAcquire(BufMgrLock);
+ ReleaseBufferWithBufferLock(i);
}
/*
* And mark the buffer as no longer occupied by this rel.
/* Assert checks that buffer will actually get freed! */
Assert(PrivateRefCount[i - 1] == 1 &&
bufHdr->refcount == 1);
- /* ReleaseBuffer expects we do not hold the lock at entry */
- SpinRelease(BufMgrLock);
- ReleaseBuffer(i);
- SpinAcquire(BufMgrLock);
+ ReleaseBufferWithBufferLock(i);
}
/*
* And mark the buffer as no longer occupied by this rel.
bufHdr = &BufferDescriptors[buffer - 1];
Assert(PrivateRefCount[buffer - 1] > 0);
- PrivateRefCount[buffer - 1]--;
- if (PrivateRefCount[buffer - 1] == 0)
+ if (PrivateRefCount[buffer - 1] > 1)
+ {
+ PrivateRefCount[buffer - 1]--;
+ }
+ else
{
SpinAcquire(BufMgrLock);
+ PrivateRefCount[buffer - 1] = 0;
Assert(bufHdr->refcount > 0);
bufHdr->refcount--;
if (bufHdr->refcount == 0)
return STATUS_OK;
}
+/*
+ * ReleaseBufferWithBufferLock
+ * Same as ReleaseBuffer except we hold the lock
+ */
+static int
+ReleaseBufferWithBufferLock(Buffer buffer)
+{
+ BufferDesc *bufHdr;
+
+ if (BufferIsLocal(buffer))
+ {
+ Assert(LocalRefCount[-buffer - 1] > 0);
+ LocalRefCount[-buffer - 1]--;
+ return STATUS_OK;
+ }
+
+ if (BAD_BUFFER_ID(buffer))
+ return STATUS_ERROR;
+
+ bufHdr = &BufferDescriptors[buffer - 1];
+
+ Assert(PrivateRefCount[buffer - 1] > 0);
+ if (PrivateRefCount[buffer - 1] > 1)
+ {
+ PrivateRefCount[buffer - 1]--;
+ }
+ else
+ {
+ PrivateRefCount[buffer - 1] = 0;
+ Assert(bufHdr->refcount > 0);
+ bufHdr->refcount--;
+ if (bufHdr->refcount == 0)
+ {
+ AddBufferToFreelist(bufHdr);
+ bufHdr->flags |= BM_FREE;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+
#ifdef NOT_USED
void
IncrBufferRefCount_Debug(char *file, int line, Buffer buffer)
SpinRelease(BufMgrLock);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- PrivateRefCount[buffer - 1]--;
SpinAcquire(BufMgrLock);
+ PrivateRefCount[buffer - 1] = 0;
Assert(bufHdr->refcount > 0);
bufHdr->flags |= (BM_DIRTY | BM_JUST_DIRTIED);
bufHdr->CleanupFunc = CleanupFunc;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.28 2000/12/29 21:31:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.29 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/time.h>
#include <unistd.h>
+#include "miscadmin.h"
#include "storage/s_lock.h"
/*
* If you are thinking of changing this code, be careful. This same
* loop logic is used in other places that call TAS() directly.
+ *
+ * While waiting for a lock, we check for cancel/die interrupts (which
+ * is a no-op if we are inside a critical section). The interrupt check
+ * can be omitted in places that know they are inside a critical section.
+ * Note that an interrupt must NOT be accepted after acquiring the lock.
*/
while (TAS(lock))
{
s_lock_sleep(spins++, 0, lock, file, line);
+ CHECK_FOR_INTERRUPTS();
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.59 2001/01/07 04:30:41 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.60 2001/01/14 05:08:15 tgl Exp $
*
* NOTES
*
* to close up shop already. Note that the signal handlers will not
* set these flags again, now that proc_exit_inprogress is set.
*/
- QueryCancel = false;
+ InterruptPending = false;
ProcDiePending = false;
+ QueryCancelPending = false;
+ /* And let's just make *sure* we're not interrupted ... */
+ ImmediateInterruptOK = false;
+ CritSectionCount = 1;
if (DebugLvl > 1)
elog(DEBUG, "proc_exit(%d)", code);
/* IpcSemaphoreLock(semId, sem) - locks a semaphore */
/****************************************************************************/
void
-IpcSemaphoreLock(IpcSemaphoreId semId, int sem)
+IpcSemaphoreLock(IpcSemaphoreId semId, int sem, bool interruptOK)
{
int errStatus;
struct sembuf sops;
* Note: if errStatus is -1 and errno == EINTR then it means we
* returned from the operation prematurely because we were
* sent a signal. So we try and lock the semaphore again.
- * ----------------
+ *
+ * Each time around the loop, we check for a cancel/die interrupt.
+ * We assume that if such an interrupt comes in while we are waiting,
+ * it will cause the semop() call to exit with errno == EINTR, so that
+ * we will be able to service the interrupt (if not in a critical
+ * section already).
+ *
+ * Once we acquire the lock, we do NOT check for an interrupt before
+ * returning. The caller needs to be able to record ownership of
+ * the lock before any interrupt can be accepted.
+ *
+ * There is a window of a few instructions between CHECK_FOR_INTERRUPTS
+ * and entering the semop() call. If a cancel/die interrupt occurs in
+ * that window, we would fail to notice it until after we acquire the
+ * lock (or get another interrupt to escape the semop()). We can avoid
+ * this problem by temporarily setting ImmediateInterruptOK = true
+ * before we do CHECK_FOR_INTERRUPTS; then, a die() interrupt in this
+ * interval will execute directly. However, there is a huge pitfall:
+ * there is another window of a few instructions after the semop()
+ * before we are able to reset ImmediateInterruptOK. If an interrupt
+ * occurs then, we'll lose control, which means that the lock has been
+ * acquired but our caller did not get a chance to record the fact.
+ * Therefore, we only set ImmediateInterruptOK if the caller tells us
+ * it's OK to do so, ie, the caller does not need to record acquiring
+ * the lock. (This is currently true for lockmanager locks, since the
+ * process that granted us the lock did all the necessary state updates.
+ * It's not true for SysV semaphores used to emulate spinlocks --- but
+ * our performance on such platforms is so horrible anyway that I'm
+ * not going to worry too much about it.)
+ * ----------------
*/
do
{
+ ImmediateInterruptOK = interruptOK;
+ CHECK_FOR_INTERRUPTS();
errStatus = semop(semId, &sops, 1);
+ ImmediateInterruptOK = false;
} while (errStatus == -1 && errno == EINTR);
if (errStatus == -1)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.28 2001/01/12 21:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.29 2001/01/14 05:08:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/sem.h>
#endif
+#include "miscadmin.h"
#include "storage/proc.h"
#include "storage/s_lock.h"
+
/* Probably should move these to an appropriate header file */
extern SPINLOCK ShmemLock;
extern SPINLOCK ShmemIndexLock;
SLock *slckP = &(SLockArray[lockid]);
PRINT_SLDEBUG("SpinAcquire", lockid, slckP);
- /*
- * Lock out die() until we exit the critical section protected by the
- * spinlock. This ensures that die() will not interrupt manipulations
- * of data structures in shared memory. We don't want die() to
- * interrupt this routine between S_LOCK and PROC_INCR_SLOCK, either,
- * so must do it before acquiring the lock, not after.
- */
- START_CRIT_SECTION();
/*
* Acquire the lock, then record that we have done so (for recovery
- * in case of elog(ERROR) during the critical section).
+ * in case of elog(ERROR) during the critical section). Note we assume
+ * here that S_LOCK will not accept cancel/die interrupts once it has
+ * acquired the lock. However, interrupts should be accepted while
+ * waiting, if CritSectionCount is zero.
*/
S_LOCK(&(slckP->shlock));
PROC_INCR_SLOCK(lockid);
+ /*
+ * Lock out cancel/die interrupts until we exit the critical section
+ * protected by the spinlock. This ensures that interrupts will not
+ * interfere with manipulations of data structures in shared memory.
+ */
+ START_CRIT_SECTION();
PRINT_SLDEBUG("SpinAcquire/done", lockid, slckP);
}
void
SpinAcquire(SPINLOCK lock)
{
- /* See the TAS() version of this routine for commentary */
- START_CRIT_SECTION();
- IpcSemaphoreLock(SpinLockIds[0], lock);
+ /*
+ * See the TAS() version of this routine for primary commentary.
+ *
+ * NOTE we must pass interruptOK = false to IpcSemaphoreLock, to ensure
+ * that a cancel/die interrupt cannot prevent us from recording ownership
+ * of a lock we have just acquired.
+ */
+ IpcSemaphoreLock(SpinLockIds[0], lock, false);
PROC_INCR_SLOCK(lock);
+ START_CRIT_SECTION();
}
/*
semval = IpcSemaphoreGetValue(SpinLockIds[0], lock);
Assert(semval < 1);
- Assert(!MyProc || MyProc->sLocks[lockid] > 0);
#endif
+ Assert(!MyProc || MyProc->sLocks[lockid] > 0);
PROC_DECR_SLOCK(lock);
IpcSemaphoreUnlock(SpinLockIds[0], lock);
END_CRIT_SECTION();
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.76 2001/01/10 01:24:19 inoue Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.77 2001/01/14 05:08:15 tgl Exp $
*
* NOTES
* Outside modules can create a lock table and acquire/release
*/
status = WaitOnLock(lockmethod, lockmode, lock, holder);
+ /*
+ * NOTE: do not do any material change of state between here and
+ * return. All required changes in locktable state must have been
+ * done when the lock was granted to us --- see notes in WaitOnLock.
+ */
+
/*
* Check the holder entry status, in case something in the ipc
* communication doesn't work correctly.
lock->nActive++;
lock->activeHolders[lockmode]++;
lock->mask |= BITS_ON[lockmode];
+ if (lock->activeHolders[lockmode] == lock->holders[lockmode])
+ lock->waitMask &= BITS_OFF[lockmode];
LOCK_PRINT("GrantLock", lock, lockmode);
Assert((lock->nActive > 0) && (lock->activeHolders[lockmode] > 0));
Assert(lock->nActive <= lock->nHolding);
strcat(new_status, " waiting");
set_ps_display(new_status);
+ /*
+ * NOTE: Think not to put any lock state cleanup after the call to
+ * ProcSleep, in either the normal or failure path. The lock state
+ * must be fully set by the lock grantor, or by HandleDeadlock if we
+ * give up waiting for the lock. This is necessary because of the
+ * possibility that a cancel/die interrupt will interrupt ProcSleep
+ * after someone else grants us the lock, but before we've noticed it.
+ * Hence, after granting, the locktable state must fully reflect the
+ * fact that we own the lock; we can't do additional work on return.
+ */
+
if (ProcSleep(lockMethodTable->ctl,
lockmode,
lock,
{
/* -------------------
* We failed as a result of a deadlock, see HandleDeadLock().
- * Decrement the lock nHolding and holders fields as
- * we are no longer waiting on this lock. Removal of the holder and
- * lock objects, if no longer needed, will happen in xact cleanup.
+ * Quit now. Removal of the holder and lock objects, if no longer
+ * needed, will happen in xact cleanup (see above for motivation).
* -------------------
*/
- lock->nHolding--;
- lock->holders[lockmode]--;
LOCK_PRINT("WaitOnLock: aborting on lock", lock, lockmode);
- Assert((lock->nHolding >= 0) && (lock->holders[lockmode] >= 0));
- Assert(lock->nActive <= lock->nHolding);
- if (lock->activeHolders[lockmode] == lock->holders[lockmode])
- lock->waitMask &= BITS_OFF[lockmode];
SpinRelease(lockMethodTable->ctl->masterLock);
elog(ERROR, DeadLockMessage);
/* not reached */
}
- if (lock->activeHolders[lockmode] == lock->holders[lockmode])
- lock->waitMask &= BITS_OFF[lockmode];
-
set_ps_display(old_status);
pfree(old_status);
pfree(new_status);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.91 2001/01/12 21:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.92 2001/01/14 05:08:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* This is so that we can support more backends. (system-wide semaphore
* sets run out pretty fast.) -ay 4/95
*
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.91 2001/01/12 21:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.92 2001/01/14 05:08:16 tgl Exp $
*/
#include "postgres.h"
#include "storage/proc.h"
-
-void HandleDeadLock(SIGNAL_ARGS);
-static void ProcFreeAllSemaphores(void);
-static bool GetOffWaitQueue(PROC *);
-
int DeadlockTimeout = 1000;
/* --------------------
PROC *MyProc = NULL;
-static void ProcKill(int exitStatus, Datum pid);
+static bool waitingForLock = false;
+
+static void ProcKill(void);
static void ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum);
static void ProcFreeSem(IpcSemaphoreId semId, int semNum);
+static void ZeroProcSemaphore(PROC *proc);
+static void ProcFreeAllSemaphores(void);
+
/*
* InitProcGlobal -
MemSet(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
MyProc->sLocks[ProcStructLock] = 1;
+ /*
+ * Set up a wait-semaphore for the proc.
+ */
if (IsUnderPostmaster)
{
- IpcSemaphoreId semId;
- int semNum;
- union semun semun;
-
- ProcGetNewSemIdAndNum(&semId, &semNum);
-
+ ProcGetNewSemIdAndNum(&MyProc->sem.semId, &MyProc->sem.semNum);
/*
* we might be reusing a semaphore that belongs to a dead backend.
* So be careful and reinitialize its value here.
*/
- semun.val = 1;
- semctl(semId, semNum, SETVAL, semun);
-
- IpcSemaphoreLock(semId, semNum);
- MyProc->sem.semId = semId;
- MyProc->sem.semNum = semNum;
+ ZeroProcSemaphore(MyProc);
}
else
+ {
MyProc->sem.semId = -1;
+ MyProc->sem.semNum = -1;
+ }
MyProc->pid = MyProcPid;
MyProc->databaseId = MyDatabaseId;
* -------------------------
*/
location = MAKE_OFFSET(MyProc);
- if ((!ShmemPIDLookup(MyProcPid, &location)) || (location != MAKE_OFFSET(MyProc)))
+ if ((!ShmemPIDLookup(MyProcPid, &location)) ||
+ (location != MAKE_OFFSET(MyProc)))
elog(STOP, "InitProcess: ShmemPID table broken");
MyProc->errType = NO_ERROR;
SHMQueueElemInit(&(MyProc->links));
- on_shmem_exit(ProcKill, (Datum) MyProcPid);
+ on_shmem_exit(ProcKill, 0);
}
-/* -----------------------
- * get process off any wait queue it might be on
+/*
+ * Initialize the proc's wait-semaphore to count zero.
+ */
+static void
+ZeroProcSemaphore(PROC *proc)
+{
+ union semun semun;
+
+ semun.val = 0;
+ if (semctl(proc->sem.semId, proc->sem.semNum, SETVAL, semun) < 0)
+ {
+ fprintf(stderr, "ZeroProcSemaphore: semctl(id=%d,SETVAL) failed: %s\n",
+ proc->sem.semId, strerror(errno));
+ proc_exit(255);
+ }
+}
+
+/*
+ * Remove a proc from the wait-queue it is on
+ * (caller must know it is on one).
+ * Locktable lock must be held by caller.
*
* NB: this does not remove the process' holder object, nor the lock object,
* even though their holder counts might now have gone to zero. That will
* happen during a subsequent LockReleaseAll call, which we expect will happen
* during transaction cleanup. (Removal of a proc from its wait queue by
* this routine can only happen if we are aborting the transaction.)
- * -----------------------
*/
-static bool
-GetOffWaitQueue(PROC *proc)
+static void
+RemoveFromWaitQueue(PROC *proc)
{
- bool gotoff = false;
+ LOCK *waitLock = proc->waitLock;
+ LOCKMODE lockmode = proc->waitLockMode;
- LockLockTable();
- if (proc->links.next != INVALID_OFFSET)
+ /* Make sure proc is waiting */
+ Assert(proc->links.next != INVALID_OFFSET);
+ Assert(waitLock);
+ Assert(waitLock->waitProcs.size > 0);
+
+ /* Remove proc from lock's wait queue */
+ SHMQueueDelete(&(proc->links));
+ waitLock->waitProcs.size--;
+
+ /* Undo increments of holder counts by waiting process */
+ Assert(waitLock->nHolding > 0);
+ Assert(waitLock->nHolding > proc->waitLock->nActive);
+ waitLock->nHolding--;
+ Assert(waitLock->holders[lockmode] > 0);
+ waitLock->holders[lockmode]--;
+ /* don't forget to clear waitMask bit if appropriate */
+ if (waitLock->activeHolders[lockmode] == waitLock->holders[lockmode])
+ waitLock->waitMask &= ~(1 << lockmode);
+
+ /* Clean up the proc's own state */
+ SHMQueueElemInit(&(proc->links));
+ proc->waitLock = NULL;
+ proc->waitHolder = NULL;
+
+ /* See if any other waiters for the lock can be woken up now */
+ ProcLockWakeup(LOCK_LOCKMETHOD(*waitLock), waitLock);
+}
+
+/*
+ * Cancel any pending wait for lock, when aborting a transaction.
+ *
+ * (Normally, this would only happen if we accept a cancel/die
+ * interrupt while waiting; but an elog(ERROR) while waiting is
+ * within the realm of possibility, too.)
+ */
+void
+LockWaitCancel(void)
+{
+ /* Nothing to do if we weren't waiting for a lock */
+ if (!waitingForLock)
+ return;
+ waitingForLock = false;
+
+ /* Turn off the deadlock timer, if it's still running (see ProcSleep) */
+#ifndef __BEOS__
{
- LOCK *waitLock = proc->waitLock;
- LOCKMODE lockmode = proc->waitLockMode;
-
- /* Remove proc from lock's wait queue */
- Assert(waitLock);
- Assert(waitLock->waitProcs.size > 0);
- SHMQueueDelete(&(proc->links));
- --waitLock->waitProcs.size;
-
- /* Undo increments of holder counts by waiting process */
- Assert(waitLock->nHolding > 0);
- Assert(waitLock->nHolding > proc->waitLock->nActive);
- --waitLock->nHolding;
- Assert(waitLock->holders[lockmode] > 0);
- --waitLock->holders[lockmode];
- /* don't forget to clear waitMask bit if appropriate */
- if (waitLock->activeHolders[lockmode] == waitLock->holders[lockmode])
- waitLock->waitMask &= ~(1 << lockmode);
-
- /* Clean up the proc's own state */
- SHMQueueElemInit(&(proc->links));
- proc->waitLock = NULL;
- proc->waitHolder = NULL;
-
- /* See if any other waiters can be woken up now */
- ProcLockWakeup(LOCK_LOCKMETHOD(*waitLock), waitLock);
-
- gotoff = true;
+ struct itimerval timeval,
+ dummy;
+
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ setitimer(ITIMER_REAL, &timeval, &dummy);
}
+#else
+ /* BeOS doesn't have setitimer, but has set_alarm */
+ set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM);
+#endif /* __BEOS__ */
+
+ /* Unlink myself from the wait queue, if on it (might not be anymore!) */
+ LockLockTable();
+ if (MyProc->links.next != INVALID_OFFSET)
+ RemoveFromWaitQueue(MyProc);
UnlockLockTable();
- return gotoff;
+ /*
+ * Reset the proc wait semaphore to zero. This is necessary in the
+ * scenario where someone else granted us the lock we wanted before we
+ * were able to remove ourselves from the wait-list. The semaphore will
+ * have been bumped to 1 by the would-be grantor, and since we are no
+ * longer going to wait on the sema, we have to force it back to zero.
+ * Otherwise, our next attempt to wait for a lock will fall through
+ * prematurely.
+ */
+ ZeroProcSemaphore(MyProc);
}
+
/*
* ProcReleaseLocks() -- release locks associated with current transaction
* at transaction commit or abort
{
if (!MyProc)
return;
- GetOffWaitQueue(MyProc);
+ /* If waiting, get off wait queue (should only be needed after error) */
+ LockWaitCancel();
+ /* Release locks */
LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc,
!isCommit, GetCurrentTransactionId());
}
/*
* ProcRemove -
- * used by the postmaster to clean up the global tables. This also frees
- * up the semaphore used for the lmgr of the process.
+ * called by the postmaster to clean up the global tables after a
+ * backend exits. This also frees up the proc's wait semaphore.
*/
bool
ProcRemove(int pid)
SHMEM_OFFSET location;
PROC *proc;
- location = INVALID_OFFSET;
-
location = ShmemPIDDestroy(pid);
if (location == INVALID_OFFSET)
return FALSE;
/*
* ProcKill() -- Destroy the per-proc data structure for
* this process. Release any of its held spin locks.
+ *
+ * This is done inside the backend process before it exits.
+ * ProcRemove, above, will be done by the postmaster afterwards.
*/
static void
-ProcKill(int exitStatus, Datum pid)
+ProcKill(void)
{
- PROC *proc;
-
- if ((int) pid == MyProcPid)
- {
- proc = MyProc;
- MyProc = NULL;
- }
- else
- {
- /* This path is dead code at the moment ... */
- SHMEM_OFFSET location = INVALID_OFFSET;
-
- ShmemPIDLookup((int) pid, &location);
- if (location == INVALID_OFFSET)
- return;
- proc = (PROC *) MAKE_PTR(location);
- }
-
- Assert(proc);
+ Assert(MyProc);
- /* Release any spinlocks the proc is holding */
- ProcReleaseSpins(proc);
+ /* Release any spinlocks I am holding */
+ ProcReleaseSpins(MyProc);
- /* Get the proc off any wait queue it might be on */
- GetOffWaitQueue(proc);
+ /* Get off any wait queue I might be on */
+ LockWaitCancel();
/* Remove from the standard lock table */
- LockReleaseAll(DEFAULT_LOCKMETHOD, proc, true, InvalidTransactionId);
+ LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, true, InvalidTransactionId);
#ifdef USER_LOCKS
/* Remove from the user lock table */
- LockReleaseAll(USER_LOCKMETHOD, proc, true, InvalidTransactionId);
+ LockReleaseAll(USER_LOCKMETHOD, MyProc, true, InvalidTransactionId);
#endif
+
+ MyProc = NULL;
}
/*
}
-/*
- * Handling cancel request while waiting for lock
- *
- */
-static bool lockWaiting = false;
-
-void
-SetWaitingForLock(bool waiting)
-{
- if (waiting == lockWaiting)
- return;
- lockWaiting = waiting;
- if (lockWaiting)
- {
- /* The lock was already released ? */
- if (MyProc->links.next == INVALID_OFFSET)
- {
- lockWaiting = false;
- return;
- }
- if (QueryCancel) /* cancel request pending */
- {
- if (GetOffWaitQueue(MyProc))
- {
- lockWaiting = false;
- elog(ERROR, "Query cancel requested while waiting for lock");
- }
- }
- }
-}
-
-void
-LockWaitCancel(void)
-{
-#ifndef __BEOS__
- struct itimerval timeval,
- dummy;
-
- if (!lockWaiting)
- return;
- lockWaiting = false;
- /* Deadlock timer off */
- MemSet(&timeval, 0, sizeof(struct itimerval));
- setitimer(ITIMER_REAL, &timeval, &dummy);
-#else
- /* BeOS doesn't have setitimer, but has set_alarm */
- if (!lockWaiting)
- return;
- lockWaiting = false;
- /* Deadlock timer off */
- set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM);
-#endif /* __BEOS__ */
-
- if (GetOffWaitQueue(MyProc))
- elog(ERROR, "Query cancel requested while waiting for lock");
-}
-
/*
* ProcSleep -- put a process to sleep
*
* P() on the semaphore should put us to sleep. The process
- * semaphore is cleared by default, so the first time we try
- * to acquire it, we sleep.
+ * semaphore is normally zero, so when we try to acquire it, we sleep.
+ *
+ * Locktable's spinlock must be held at entry, and will be held
+ * at exit.
*
* Result is NO_ERROR if we acquired the lock, STATUS_ERROR if not (deadlock).
*
ins:;
/* -------------------
- * assume that these two operations are atomic (because
+ * Insert self into queue. These operations are atomic (because
* of the spinlock).
* -------------------
*/
MyProc->errType = NO_ERROR; /* initialize result for success */
+ /* mark that we are waiting for a lock */
+ waitingForLock = true;
+
+ /* -------------------
+ * Release the locktable's spin 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 LockWaitCancel will clean up if cancel/die happens.
+ * -------------------
+ */
SpinRelease(spinlock);
/* --------------
elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#endif
- SetWaitingForLock(true);
-
/* --------------
* If someone wakes us between SpinRelease and IpcSemaphoreLock,
* IpcSemaphoreLock will not block. The wakeup is "saved" by
* is invoked but does not detect a deadlock, IpcSemaphoreLock()
* will continue to wait. There used to be a loop here, but it
* was useless code...
+ *
+ * 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).
* --------------
*/
- IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum);
-
- lockWaiting = false;
+ IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, true);
/* ---------------
* Disable the timer, if it's still running
* ---------------
*/
#ifndef __BEOS__
- timeval.it_value.tv_sec = 0;
- timeval.it_value.tv_usec = 0;
+ MemSet(&timeval, 0, sizeof(struct itimerval));
if (setitimer(ITIMER_REAL, &timeval, &dummy))
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#else
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#endif
+ /*
+ * Now there is nothing for LockWaitCancel to do.
+ */
+ waitingForLock = false;
+
/* ----------------
- * We were assumed to be in a critical section when we went
- * to sleep.
+ * Re-acquire the locktable's spin lock.
+ *
+ * We could accept a cancel/die interrupt here. That's OK because
+ * the lock is now registered as being held by this process.
* ----------------
*/
SpinAcquire(spinlock);
/* --------------------
* We only get to this routine if we got SIGALRM after DeadlockTimeout
- * while waiting for a lock to be released by some other process. If we have
- * a real deadlock, we must also indicate that I'm no longer waiting
- * on a lock so that other processes don't try to wake me up and screw
- * up my semaphore.
+ * while waiting for a lock to be released by some other process. Look
+ * to see if there's a deadlock; if not, just return and continue waiting.
+ * If we have a real deadlock, remove ourselves from the lock's wait queue
+ * and signal an error to ProcSleep.
* --------------------
*/
void
HandleDeadLock(SIGNAL_ARGS)
{
int save_errno = errno;
- LOCK *mywaitlock;
- bool isWaitingForLock = lockWaiting; /* save waiting status */
- SetWaitingForLock(false); /* disable query cancel during this fuction */
+ /*
+ * Acquire locktable lock. Note that the SIGALRM interrupt had better
+ * not be enabled anywhere that this process itself holds the locktable
+ * lock, else this will wait forever. Also note that this calls
+ * SpinAcquire which creates a critical section, so that this routine
+ * cannot be interrupted by cancel/die interrupts.
+ */
LockLockTable();
/* ---------------------
{
UnlockLockTable();
errno = save_errno;
- SetWaitingForLock(isWaitingForLock); /* restore waiting status */
return;
}
/* No deadlock, so keep waiting */
UnlockLockTable();
errno = save_errno;
- SetWaitingForLock(isWaitingForLock); /* restore waiting status */
return;
}
/* ------------------------
- * Get this process off the lock's wait queue
+ * Oops. We have a deadlock.
+ *
+ * Get this process out of wait state.
* ------------------------
*/
- mywaitlock = MyProc->waitLock;
- Assert(mywaitlock->waitProcs.size > 0);
- --mywaitlock->waitProcs.size;
- SHMQueueDelete(&(MyProc->links));
- SHMQueueElemInit(&(MyProc->links));
- MyProc->waitLock = NULL;
- MyProc->waitHolder = NULL;
- isWaitingForLock = false; /* wait for lock no longer */
+ RemoveFromWaitQueue(MyProc);
+
+ /* -------------
+ * Set MyProc->errType to STATUS_ERROR so that ProcSleep will
+ * report an error after we return from this signal handler.
+ * -------------
+ */
+ MyProc->errType = STATUS_ERROR;
/* ------------------
* Unlock my semaphore so that the interrupted ProcSleep() call can finish.
*/
IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum);
- /* -------------
- * Set MyProc->errType to STATUS_ERROR so that we abort after
- * returning from this handler.
- * -------------
- */
- MyProc->errType = STATUS_ERROR;
-
- /*
- * if this doesn't follow the IpcSemaphoreUnlock then we get lock
- * table corruption ("LockReplace: xid table corrupted") due to race
- * conditions. i don't claim to understand this...
+ /* ------------------
+ * We're done here. Transaction abort caused by the error that ProcSleep
+ * will raise will cause any other locks we hold to be released, thus
+ * allowing other processes to wake up; we don't need to do that here.
+ * NOTE: an exception is that releasing locks we hold doesn't consider
+ * the possibility of waiters that were blocked behind us on the lock
+ * we just failed to get, and might now be wakable because we're not
+ * in front of them anymore. However, RemoveFromWaitQueue took care of
+ * waking up any such processes.
+ * ------------------
*/
UnlockLockTable();
errno = save_errno;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.200 2001/01/12 21:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.201 2001/01/14 05:08:16 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
CommandDest whereToSendOutput = Debug;
-
-extern void HandleDeadLock(SIGNAL_ARGS);
-
static bool dontExecute = false;
/* note: these declarations had better match tcopprot.h */
bool Warn_restart_ready = false;
bool InError = false;
-volatile bool ProcDiePending = false;
static bool EchoQuery = false; /* default don't echo */
char pg_pathname[MAXPGPATH];
}
/* If we got a cancel signal in parsing or prior command, quit */
- if (QueryCancel)
- CancelQuery();
+ CHECK_FOR_INTERRUPTS();
/*
* OK to analyze and rewrite this query.
}
/* If we got a cancel signal in analysis or prior command, quit */
- if (QueryCancel)
- CancelQuery();
+ CHECK_FOR_INTERRUPTS();
if (querytree->commandType == CMD_UTILITY)
{
plan = pg_plan_query(querytree);
/* if we got a cancel signal whilst planning, quit */
- if (QueryCancel)
- CancelQuery();
+ CHECK_FOR_INTERRUPTS();
/* Initialize snapshot state for query */
SetQuerySnapshot();
/* --------------------------------
* signal handler routines used in PostgresMain()
- *
- * handle_warn() catches SIGQUIT. It forces control back to the main
- * loop, just as if an internal error (elog(ERROR,...)) had occurred.
- * elog() used to actually use kill(2) to induce a SIGQUIT to get here!
- * But that's not 100% reliable on some systems, so now it does its own
- * siglongjmp() instead.
- * We still provide the signal catcher so that an error quit can be
- * forced externally. This should be done only with great caution,
- * however, since an asynchronous signal could leave the system in
- * who-knows-what inconsistent state.
- *
- * quickdie() occurs when signalled by the postmaster.
- * Some backend has bought the farm,
- * so we need to stop what we're doing and exit.
- *
- * die() performs an orderly cleanup via proc_exit()
* --------------------------------
*/
-void
-handle_warn(SIGNAL_ARGS)
-{
- /* Don't joggle the elbow of proc_exit */
- if (proc_exit_inprogress)
- return;
- /* Don't joggle the elbow of a critical section, either */
- if (CritSectionCount > 0)
- {
- QueryCancel = true;
- return;
- }
- siglongjmp(Warn_restart, 1);
-}
-
+/*
+ * quickdie() occurs when signalled SIGUSR1 by the postmaster.
+ *
+ * Some backend has bought the farm,
+ * so we need to stop what we're doing and exit.
+ */
static void
quickdie(SIGNAL_ARGS)
{
" going to terminate your database system connection and exit."
"\n\tPlease reconnect to the database system and repeat your query.");
-
/*
- * DO NOT proc_exit(0) -- we're here because shared memory may be
- * corrupted, so we don't want to flush any shared state to stable
- * storage. Just nail the windows shut and get out of town.
+ * DO NOT proc_exit() -- we're here because shared memory may be
+ * corrupted, so we don't want to try to clean up our transaction.
+ * Just nail the windows shut and get out of town.
+ *
+ * Note we do exit(1) not exit(0). This is to force the postmaster
+ * into a system reset cycle if some idiot DBA sends a manual SIGUSR1
+ * to a random backend. This is necessary precisely because we don't
+ * clean up our shared memory state.
*/
exit(1);
}
/*
- * Abort transaction and exit
+ * Shutdown signal from postmaster: abort transaction and exit
+ * at soonest convenient time
*/
void
die(SIGNAL_ARGS)
{
int save_errno = errno;
- PG_SETMASK(&BlockSig);
-
/* Don't joggle the elbow of proc_exit */
- if (proc_exit_inprogress)
- {
- errno = save_errno;
- return;
- }
- /* Don't joggle the elbow of a critical section, either */
- if (CritSectionCount > 0)
+ if (! proc_exit_inprogress)
{
+ InterruptPending = true;
ProcDiePending = true;
- errno = save_errno;
- return;
+ /*
+ * If we're waiting for input, service the interrupt immediately
+ */
+ if (ImmediateInterruptOK && CritSectionCount == 0)
+ {
+ DisableNotifyInterrupt();
+ ProcessInterrupts();
+ }
}
- /* Otherwise force immediate proc_exit */
- ForceProcDie();
+
+ errno = save_errno;
}
/*
- * This is split out of die() so that it can be invoked later from
- * END_CRIT_SECTION().
+ * Query-cancel signal from postmaster: abort current transaction
+ * at soonest convenient time
*/
-void
-ForceProcDie(void)
-{
- /* Reset flag to avoid another elog() during shutdown */
- ProcDiePending = false;
- /* Send error message and do proc_exit() */
- elog(FATAL, "The system is shutting down");
-}
-
-/* signal handler for query cancel signal from postmaster */
static void
QueryCancelHandler(SIGNAL_ARGS)
{
int save_errno = errno;
/* Don't joggle the elbow of proc_exit, nor an already-in-progress abort */
- if (proc_exit_inprogress || InError)
+ if (!proc_exit_inprogress && !InError)
{
- errno = save_errno;
- return;
+ InterruptPending = true;
+ QueryCancelPending = true;
+ /*
+ * No point in raising Cancel if we are waiting for input ...
+ */
}
- /* Set flag to cause CancelQuery to be called when it's safe */
- QueryCancel = true;
-
- /* If we happen to be waiting for a lock, get out of that */
- LockWaitCancel();
-
- /* Otherwise, bide our time... */
errno = save_errno;
}
-void
-CancelQuery(void)
-{
- /* Reset flag to avoid another elog() during error recovery */
- QueryCancel = false;
- /* Create an artificial error condition to get out of query */
- elog(ERROR, "Query was cancelled.");
-}
-
/* signal handler for floating point exception */
static void
FloatExceptionHandler(SIGNAL_ARGS)
" or was a divide by zero");
}
+/* SIGHUP: set flag to re-read config file at next convenient time */
static void
SigHupHandler(SIGNAL_ARGS)
{
}
+/*
+ * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro
+ *
+ * If an interrupt condition is pending, and it's safe to service it,
+ * then clear the flag and accept the interrupt. Called only when
+ * InterruptPending is true.
+ */
+void
+ProcessInterrupts(void)
+{
+ /* Cannot accept interrupts inside critical sections */
+ if (CritSectionCount != 0)
+ return;
+ InterruptPending = false;
+ if (ProcDiePending)
+ {
+ ProcDiePending = false;
+ QueryCancelPending = false; /* ProcDie trumps QueryCancel */
+ ImmediateInterruptOK = false; /* not idle anymore */
+ elog(FATAL, "The system is shutting down");
+ }
+ if (QueryCancelPending)
+ {
+ QueryCancelPending = false;
+ ImmediateInterruptOK = false; /* not idle anymore */
+ elog(ERROR, "Query was cancelled.");
+ }
+ /* If we get here, do nothing (probably, QueryCancelPending was reset) */
+}
+
static void
usage(char *progname)
*/
pqsignal(SIGHUP, SigHupHandler); /* set flag to read config file */
- pqsignal(SIGINT, QueryCancelHandler); /* cancel current query */
- pqsignal(SIGQUIT, handle_warn); /* handle error */
- pqsignal(SIGTERM, die);
+ pqsignal(SIGINT, QueryCancelHandler); /* cancel current query */
+ pqsignal(SIGTERM, die); /* cancel current query and exit */
+ pqsignal(SIGQUIT, die); /* could reassign this sig for another use */
pqsignal(SIGALRM, HandleDeadLock);
/*
pqsignal(SIGUSR1, quickdie);
pqsignal(SIGUSR2, Async_NotifyHandler); /* flush also sinval cache */
pqsignal(SIGFPE, FloatExceptionHandler);
- pqsignal(SIGCHLD, SIG_IGN); /* ignored, sent by LockOwners */
+ pqsignal(SIGCHLD, SIG_IGN); /* ignored (may get this in system() calls) */
+
+ /*
+ * Reset some signals that are accepted by postmaster but not by backend
+ */
pqsignal(SIGTTIN, SIG_DFL);
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
+ pqsignal(SIGWINCH, SIG_DFL);
pqinitmask();
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.200 $ $Date: 2001/01/12 21:53:59 $\n");
+ puts("$Revision: 1.201 $ $Date: 2001/01/14 05:08:16 $\n");
}
/*
* consider the probability that it should be in AbortTransaction()
* instead.
*
+ * Make sure we're not interrupted while cleaning up. Also forget
+ * any pending QueryCancel request, since we're aborting anyway.
+ * Force CritSectionCount to a known state in case we elog'd
+ * from inside a critical section.
+ */
+ ImmediateInterruptOK = false;
+ QueryCancelPending = false;
+ CritSectionCount = 1;
+
+ /*
* Make sure we are in a valid memory context during recovery.
*
* We use ErrorContext in hopes that it will have some free space
* successfully. (Flag was set in elog.c before longjmp().)
*/
InError = false;
+
+ /*
+ * Exit critical section we implicitly established above.
+ * (This could result in accepting a cancel or die interrupt.)
+ */
+ END_CRIT_SECTION();
}
Warn_restart_ready = true; /* we can now handle elog(ERROR) */
/* ----------------
* (2) deal with pending asynchronous NOTIFY from other backends,
* and enable async.c's signal handler to execute NOTIFY directly.
+ * Then set up other stuff needed before blocking for input.
* ----------------
*/
- QueryCancel = false; /* forget any earlier CANCEL signal */
- SetWaitingForLock(false);
+ QueryCancelPending = false; /* forget any earlier CANCEL signal */
EnableNotifyInterrupt();
+ set_ps_display("idle");
+
+ /* Allow "die" interrupt to be processed while waiting */
+ ImmediateInterruptOK = true;
+ /* and don't forget to detect one that already arrived */
+ QueryCancelPending = false;
+ CHECK_FOR_INTERRUPTS();
+
/* ----------------
* (3) read a command (loop blocks here)
* ----------------
*/
- set_ps_display("idle");
-
firstchar = ReadCommand(parser_input);
- QueryCancel = false; /* forget any earlier CANCEL signal */
-
/* ----------------
- * (4) disable async.c's signal handler.
+ * (4) disable async signal conditions again.
* ----------------
*/
+ ImmediateInterruptOK = false;
+ QueryCancelPending = false; /* forget any CANCEL signal */
+
DisableNotifyInterrupt();
/* ----------------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.75 2001/01/09 18:40:14 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.76 2001/01/14 05:08:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *msg_buf = msg_fixedbuf;
/* this buffer is only used for strange values of lev: */
char prefix_buf[32];
+#ifdef HAVE_SYS_NERR
/* this buffer is only used if errno has a bogus value: */
char errorstr_buf[32];
+#endif
const char *errorstr;
const char *prefix;
const char *cp;
errorstr = errorstr_buf;
}
#else
+ /* assume strerror() will cope gracefully with bogus errno values */
errorstr = strerror(errno);
#endif
- if (lev == ERROR || lev == FATAL)
- {
- /* this is probably redundant... */
- if (IsInitProcessingMode())
- lev = FATAL;
- if (CritSectionCount > 0)
- lev = STOP;
- }
+ /* Convert initialization errors into fatal errors.
+ * This is probably redundant, because Warn_restart_ready won't
+ * be set anyway...
+ */
+ if (lev == ERROR && IsInitProcessingMode())
+ lev = FATAL;
/* choose message prefix and indent level */
switch (lev)
if (Debugfile >= 0 && Use_syslog <= 1)
write(Debugfile, msg_buf, len);
-#ifndef PG_STANDALONE
-
if (lev > DEBUG && whereToSendOutput == Remote)
{
/* Send IPC message to the front-end program */
fputs(msg_buf, stderr);
}
-#endif /* !PG_STANDALONE */
-
/* done with the message, release space */
if (fmt_buf != fmt_fixedbuf)
free(fmt_buf);
*/
if (lev == ERROR || lev == FATAL)
{
+ /* Prevent immediate interrupt while entering error recovery */
+ ImmediateInterruptOK = false;
/*
* For a FATAL error, we let proc_exit clean up and exit.
if (lev > FATAL)
{
-
/*
* Serious crash time. Postmaster will observe nonzero process
* exit status and kill the other backends too.
* XXX: what if we are *in* the postmaster? proc_exit() won't kill
* our children...
*/
+ ImmediateInterruptOK = false;
fflush(stdout);
fflush(stderr);
proc_exit(lev);
/* We reach here if lev <= NOTICE. OK to return to caller. */
}
-#ifndef PG_STANDALONE
-
int
DebugFileOpen(void)
{
return Debugfile;
}
-#endif
-
-
/*
* Return a timestamp string like
#ifdef ENABLE_SYSLOG
+#ifndef PG_SYSLOG_LIMIT
+# define PG_SYSLOG_LIMIT 128
+#endif
+
/*
* Write a message line to syslog if the syslog option is set.
*
static void
write_syslog(int level, const char *line)
{
-#ifndef PG_SYSLOG_LIMIT
-# define PG_SYSLOG_LIMIT 128
-#endif
-
static bool openlog_done = false;
static unsigned long seq = 0;
static int syslog_fac = LOG_LOCAL0;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.49 2001/01/07 04:17:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.50 2001/01/14 05:08:16 tgl Exp $
*
* NOTES
* Globals used all over the place should be declared here and not
bool Noversion = false;
bool Quiet = false;
-volatile bool QueryCancel = false;
+
+volatile bool InterruptPending = false;
+volatile bool QueryCancelPending = false;
+volatile bool ProcDiePending = false;
+volatile bool ImmediateInterruptOK = false;
+volatile uint32 CritSectionCount = 0;
int MyProcPid;
struct Port *MyProcPort;
char *DatabaseName = NULL;
char *DatabasePath = NULL;
-bool MyDatabaseIdIsInitialized = false;
Oid MyDatabaseId = InvalidOid;
-bool TransactionInitWasProcessed = false;
bool IsUnderPostmaster = false;
*
* PostgreSQL transaction log manager
*
- * $Header: /cvsroot/pgsql/src/include/access/xlog.h,v 1.16 2001/01/12 21:54:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/include/access/xlog.h,v 1.17 2001/01/14 05:08:16 tgl Exp $
*/
#ifndef XLOG_H
#define XLOG_H
extern StartUpID ThisStartUpID; /* current SUI */
extern bool InRecovery;
extern XLogRecPtr MyLastRecPtr;
-extern volatile uint32 CritSectionCount;
typedef struct RmgrData
{
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: miscadmin.h,v 1.76 2001/01/07 04:17:28 tgl Exp $
+ * $Id: miscadmin.h,v 1.77 2001/01/14 05:08:16 tgl Exp $
*
* NOTES
- * some of the information in this file will be moved to
+ * some of the information in this file should be moved to
* other files.
*
*-------------------------------------------------------------------------
#ifndef MISCADMIN_H
#define MISCADMIN_H
-#include <sys/types.h> /* For pid_t */
-
-#include "postgres.h"
#include "storage/ipc.h"
+/*****************************************************************************
+ * System interrupt handling
+ *
+ * There are two types of interrupts that a running backend needs to accept
+ * without messing up its state: QueryCancel (SIGINT) and ProcDie (SIGTERM).
+ * In both cases, we need to be able to clean up the current transaction
+ * gracefully, so we can't respond to the interrupt instantaneously ---
+ * there's no guarantee that internal data structures would be self-consistent
+ * if the code is interrupted at an arbitrary instant. Instead, the signal
+ * handlers set flags that are checked periodically during execution.
+ *
+ * The CHECK_FOR_INTERRUPTS() macro is called at strategically located spots
+ * where it is normally safe to accept a cancel or die interrupt. In some
+ * cases, we invoke CHECK_FOR_INTERRUPTS() inside low-level subroutines that
+ * might sometimes be called in contexts that do *not* want to allow a cancel
+ * or die interrupt. The CRIT_SECTION mechanism allows code to ensure that
+ * no cancel or die interrupt will be accepted, even if CHECK_FOR_INTERRUPTS
+ * gets called in a subroutine.
+ *
+ * Special mechanisms are used to let an interrupt be accepted when we are
+ * waiting for a lock or spinlock, and when we are waiting for command input
+ * (but, of course, only if the critical section counter is zero). See the
+ * related code for details.
+ *
+ *****************************************************************************/
+
+/* in globals.c */
+/* these are marked volatile because they are set by signal handlers: */
+extern volatile bool InterruptPending;
+extern volatile bool QueryCancelPending;
+extern volatile bool ProcDiePending;
+/* these are marked volatile because they are examined by signal handlers: */
+extern volatile bool ImmediateInterruptOK;
+extern volatile uint32 CritSectionCount;
+
+/* in postgres.c */
+extern void ProcessInterrupts(void);
+
+#define CHECK_FOR_INTERRUPTS() \
+ do { \
+ if (InterruptPending) \
+ ProcessInterrupts(); \
+ } while(0)
+
+#define START_CRIT_SECTION() (CritSectionCount++)
+
+#define END_CRIT_SECTION() \
+ do { \
+ Assert(CritSectionCount > 0); \
+ CritSectionCount--; \
+ if (CritSectionCount == 0 && InterruptPending) \
+ ProcessInterrupts(); \
+ } while(0)
+
+
/*****************************************************************************
* globals.h -- *
*****************************************************************************/
*/
extern bool Noversion;
extern bool Quiet;
-extern volatile bool QueryCancel;
extern char *DataDir;
extern int MyProcPid;
*
* extern BackendId MyBackendId;
*/
-extern bool MyDatabaseIdIsInitialized;
extern Oid MyDatabaseId;
-extern bool TransactionInitWasProcessed;
extern bool IsUnderPostmaster;
extern void SetDataDir(const char *dir);
-extern int FindExec(char *full_path, const char *argv0, const char *binary_name);
+extern int FindExec(char *full_path, const char *argv0,
+ const char *binary_name);
extern int CheckPathAccess(char *path, char *name, int open_mode);
#ifdef CYR_RECODE
/*
* Description:
* There are three processing modes in POSTGRES. They are
- * "BootstrapProcessing or "bootstrap," InitProcessing or
+ * BootstrapProcessing or "bootstrap," InitProcessing or
* "initialization," and NormalProcessing or "normal."
*
* The first two processing modes are used during special times. When the
* system state indicates bootstrap processing, transactions are all given
- * transaction id "one" and are consequently guarenteed to commit. This mode
+ * transaction id "one" and are consequently guaranteed to commit. This mode
* is used during the initial generation of template databases.
*
- * Initialization mode until all normal initialization is complete.
- * Some code behaves differently when executed in this mode to enable
- * system bootstrapping.
+ * Initialization mode: used while starting a backend, until all normal
+ * initialization is complete. Some code behaves differently when executed
+ * in this mode to enable system bootstrapping.
*
* If a POSTGRES binary is in normal mode, then all code may be executed
* normally.
* pinit.h -- *
* POSTGRES initialization and cleanup definitions. *
*****************************************************************************/
-/*
- * Note:
- * XXX AddExitHandler not defined yet.
- */
-
-typedef int16 ExitStatus;
-
-#define NormalExitStatus (0)
-#define FatalExitStatus (127)
-/* XXX are there any other meaningful exit codes? */
/* in utils/init/postinit.c */
-
extern int lockingOff;
extern void InitPostgres(const char *dbname, const char *username);
extern void BaseInit(void);
-/* one of the ways to get out of here */
-#define ExitPostgres(status) proc_exec(status)
-
/* processing mode support stuff */
extern ProcessingMode Mode;
#define SetProcessingMode(mode) \
do { \
- AssertArg(mode == BootstrapProcessing || mode == InitProcessing || \
- mode == NormalProcessing); \
- Mode = mode; \
+ AssertArg((mode) == BootstrapProcessing || \
+ (mode) == InitProcessing || \
+ (mode) == NormalProcessing); \
+ Mode = (mode); \
} while(0)
#define GetProcessingMode() Mode
-extern void IgnoreSystemIndexes(bool mode);
-extern bool IsIgnoringSystemIndexes(void);
-extern bool IsCacheInitialized(void);
-extern void SetWaitingForLock(bool);
-
extern bool CreateDataDirLockFile(const char *datadir, bool amPostmaster);
extern bool CreateSocketLockFile(const char *socketfile, bool amPostmaster);
extern void ValidatePgVersion(const char *path);
+/* these externs do not belong here... */
+extern void IgnoreSystemIndexes(bool mode);
+extern bool IsIgnoringSystemIndexes(void);
+extern bool IsCacheInitialized(void);
+
#endif /* MISCADMIN_H */
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: ipc.h,v 1.44 2000/12/03 17:18:09 tgl Exp $
+ * $Id: ipc.h,v 1.45 2001/01/14 05:08:16 tgl Exp $
*
* Some files that would normally need to include only sys/ipc.h must
* instead include this file because on Ultrix, sys/ipc.h is not designed
int semStartValue,
bool removeOnExit);
extern void IpcSemaphoreKill(IpcSemaphoreId semId);
-extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem);
+extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem, bool interruptOK);
extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem);
extern bool IpcSemaphoreTryLock(IpcSemaphoreId semId, int sem);
extern int IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: proc.h,v 1.33 2000/12/22 00:51:54 tgl Exp $
+ * $Id: proc.h,v 1.34 2001/01/14 05:08:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
typedef struct
{
- int sleeplock;
- IpcSemaphoreId semId;
- int semNum;
+ IpcSemaphoreId semId; /* SysV semaphore set ID */
+ int semNum; /* semaphore number within set */
} SEMA;
/*
SEMA sem; /* ONE semaphore to sleep on */
int errType; /* error code tells why we woke up */
- int critSects; /* If critSects > 0, we are in sensitive
- * routines that cannot be recovered when
- * the process fails. */
-
- int prio; /* priority for sleep queue */
-
TransactionId xid; /* transaction currently being executed by
* this proc */
extern PROC *MyProc;
+extern SPINLOCK ProcStructLock;
+
+
#define PROC_INCR_SLOCK(lock) \
do { \
if (MyProc) (MyProc->sLocks[(lock)])++; \
#define ERR_TIMEOUT 1
#define ERR_BUFFER_IO 2
-#define MAX_PRIO 50
-#define MIN_PRIO (-1)
-
-extern SPINLOCK ProcStructLock;
-
/*
* There is one ProcGlobal struct for the whole installation.
extern void ProcAddLock(SHM_QUEUE *elem);
extern void ProcReleaseSpins(PROC *proc);
extern void LockWaitCancel(void);
+extern void HandleDeadLock(SIGNAL_ARGS);
#endif /* PROC_H */
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: tcopprot.h,v 1.36 2000/12/03 10:27:29 vadim Exp $
+ * $Id: tcopprot.h,v 1.37 2001/01/14 05:08:16 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
#endif /* BOOTSTRAP_INCLUDE */
-extern void handle_warn(SIGNAL_ARGS);
extern void die(SIGNAL_ARGS);
-extern void CancelQuery(void);
extern int PostgresMain(int argc, char *argv[],
int real_argc, char *real_argv[], const char *username);
extern void ResetUsage(void);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: elog.h,v 1.23 2001/01/12 21:54:01 tgl Exp $
+ * $Id: elog.h,v 1.24 2001/01/14 05:08:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef ELOG_H
#define ELOG_H
+/* Error level codes */
#define NOTICE 0 /* random info - no special action */
#define ERROR (-1) /* user error - return to known state */
#define FATAL 1 /* fatal error - abort process */
#define LOG DEBUG
#define NOIND (-3) /* debug message, don't indent as far */
+/* Configurable parameters */
#ifdef ENABLE_SYSLOG
extern int Use_syslog;
#endif
-
-/*
- * If CritSectionCount > 0, signal handlers mustn't do
- * elog(ERROR|FATAL), instead remember what action is
- * required with QueryCancel or ProcDiePending.
- * ProcDiePending will be honored at critical section exit,
- * but QueryCancel is only checked at specified points.
- */
-extern volatile uint32 CritSectionCount; /* duplicates access/xlog.h */
-extern volatile bool ProcDiePending;
-extern void ForceProcDie(void); /* in postgres.c */
-
-#define START_CRIT_SECTION() (CritSectionCount++)
-
-#define END_CRIT_SECTION() \
- do { \
- Assert(CritSectionCount > 0); \
- CritSectionCount--; \
- if (CritSectionCount == 0 && ProcDiePending) \
- ForceProcDie(); \
- } while(0)
-
extern bool Log_timestamp;
extern bool Log_pid;
+
#ifndef __GNUC__
-extern void elog(int lev, const char *fmt,...);
+extern void elog(int lev, const char *fmt, ...);
#else
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
-extern void elog(int lev, const char *fmt,...) __attribute__((format(printf, 2, 3)));
+extern void elog(int lev, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
#endif
-#ifndef PG_STANDALONE
extern int DebugFileOpen(void);
-#endif
-
#endif /* ELOG_H */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.70 2000/12/15 20:01:55 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.71 2001/01/14 05:08:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
+#include "postgres.h"
+
#include <ctype.h>
#include <sys/types.h>
#include <limits.h>