From: Tom Lane Date: Sun, 14 Jan 2001 05:08:17 +0000 (+0000) Subject: Restructure backend SIGINT/SIGTERM handling so that 'die' interrupts X-Git-Tag: REL7_1~828 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=36839c192706f5abd75bdcb02b6a7cace14ce108;p=postgresql Restructure backend SIGINT/SIGTERM handling so that 'die' interrupts are treated more like 'cancel' interrupts: the signal handler sets a flag that is examined at well-defined spots, rather than trying to cope with an interrupt that might happen anywhere. See pghackers discussion of 1/12/01. --- diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 1aae86c002..852a457e2a 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/heapam.h" #include "access/nbtree.h" +#include "miscadmin.h" typedef struct diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index e3f4a5618f..e79ada3542 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -8,7 +8,7 @@ * * * 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: @@ -1015,6 +1015,9 @@ CommitTransaction(void) 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 @@ -1083,6 +1086,8 @@ CommitTransaction(void) * ---------------- */ s->state = TRANS_DEFAULT; + + END_CRIT_SECTION(); } /* -------------------------------- @@ -1095,6 +1100,9 @@ AbortTransaction(void) { 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 @@ -1113,13 +1121,21 @@ AbortTransaction(void) */ 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"); @@ -1169,6 +1185,7 @@ AbortTransaction(void) * State remains TRANS_ABORT until CleanupTransaction(). * ---------------- */ + END_CRIT_SECTION(); } /* -------------------------------- diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index e995b06f91..5777e035d4 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6,7 +6,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,6 @@ 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; diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index cd4186eec8..19b421a51d 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -329,9 +329,10 @@ BootstrapMain(int argc, char *argv[]) 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); } /* @@ -383,8 +384,6 @@ BootstrapMain(int argc, char *argv[]) * abort processing resumes here * ---------------- */ - pqsignal(SIGHUP, handle_warn); - if (sigsetjmp(Warn_restart, 1) != 0) { Warnings++; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index a1dee895b3..889cd5316e 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -72,8 +72,7 @@ analyze_rel(Oid relid, List *anal_cols2, int MESSAGE_LEVEL) * 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 diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index f33c6e9eb4..25f6bace81 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -449,8 +449,7 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp, { bool need_delim = false; - if (QueryCancel) - CancelQuery(); + CHECK_FOR_INTERRUPTS(); if (binary) { @@ -702,11 +701,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, while (!done) { - if (QueryCancel) - { - lineno = 0; - CancelQuery(); - } + CHECK_FOR_INTERRUPTS(); lineno++; diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 76425652b9..2a402004ca 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -378,8 +378,7 @@ vacuum_rel(Oid relid) * 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 diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index d7db099653..6cc2a1aed9 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -12,7 +12,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -248,14 +248,12 @@ ExecProcNode(Plan *node, Plan *parent) { TupleTableSlot *result; + CHECK_FOR_INTERRUPTS(); + /* ---------------- * deal with NULL nodes.. * ---------------- */ - - if (QueryCancel) - CancelQuery(); - if (node == NULL) return NULL; diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 2be519193b..6b89758862 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -92,6 +92,7 @@ static Buffer ReadBufferWithBufferLock(Relation relation, BlockNumber blockNum, 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); @@ -687,10 +688,14 @@ ReleaseAndReadBuffer(Buffer buffer, { 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) @@ -1185,10 +1190,7 @@ recheck: /* 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. @@ -1270,10 +1272,7 @@ recheck: /* 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. @@ -1624,10 +1623,14 @@ ReleaseBuffer(Buffer buffer) 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) @@ -1641,6 +1644,48 @@ ReleaseBuffer(Buffer buffer) 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) @@ -2217,9 +2262,9 @@ MarkBufferForCleanup(Buffer buffer, void (*CleanupFunc)(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; diff --git a/src/backend/storage/buffer/s_lock.c b/src/backend/storage/buffer/s_lock.c index 932e5b0049..00a934c383 100644 --- a/src/backend/storage/buffer/s_lock.c +++ b/src/backend/storage/buffer/s_lock.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include #include +#include "miscadmin.h" #include "storage/s_lock.h" @@ -101,10 +102,16 @@ s_lock(volatile slock_t *lock, const char *file, const int line) /* * 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(); } } diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c index d592a17986..9d796299dc 100644 --- a/src/backend/storage/ipc/ipc.c +++ b/src/backend/storage/ipc/ipc.c @@ -8,7 +8,7 @@ * * * 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 * @@ -131,8 +131,12 @@ proc_exit(int code) * 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); @@ -367,7 +371,7 @@ CallbackSemaphoreKill(int status, Datum semId) /* IpcSemaphoreLock(semId, sem) - locks a semaphore */ /****************************************************************************/ void -IpcSemaphoreLock(IpcSemaphoreId semId, int sem) +IpcSemaphoreLock(IpcSemaphoreId semId, int sem, bool interruptOK) { int errStatus; struct sembuf sops; @@ -380,11 +384,43 @@ IpcSemaphoreLock(IpcSemaphoreId semId, int sem) * 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) diff --git a/src/backend/storage/ipc/spin.c b/src/backend/storage/ipc/spin.c index ed71d79ad9..b27c181002 100644 --- a/src/backend/storage/ipc/spin.c +++ b/src/backend/storage/ipc/spin.c @@ -14,7 +14,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -25,9 +25,11 @@ #include #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; @@ -144,20 +146,21 @@ SpinAcquire(SPINLOCK lockid) 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); } @@ -317,10 +320,16 @@ SpinFreeAllSemaphores(void) 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(); } /* @@ -338,8 +347,8 @@ SpinRelease(SPINLOCK lock) 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(); diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index f15ee9f8bd..e7d1b678be 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -8,7 +8,7 @@ * * * 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 @@ -726,6 +726,12 @@ LockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag, */ 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. @@ -921,6 +927,8 @@ GrantLock(LOCK *lock, HOLDER *holder, LOCKMODE lockmode) 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); @@ -960,6 +968,17 @@ WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode, 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, @@ -967,26 +986,16 @@ WaitOnLock(LOCKMETHOD lockmethod, LOCKMODE lockmode, { /* ------------------- * 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); diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index baa31413e2..b5a22bb232 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -48,7 +48,7 @@ * 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" @@ -78,11 +78,6 @@ #include "storage/proc.h" - -void HandleDeadLock(SIGNAL_ARGS); -static void ProcFreeAllSemaphores(void); -static bool GetOffWaitQueue(PROC *); - int DeadlockTimeout = 1000; /* -------------------- @@ -98,9 +93,14 @@ static PROC_HDR *ProcGlobal = NULL; 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 - @@ -241,27 +241,23 @@ InitProcess(void) 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; @@ -282,67 +278,126 @@ InitProcess(void) * ------------------------- */ 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 @@ -360,15 +415,17 @@ ProcReleaseLocks(bool isCommit) { 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) @@ -376,8 +433,6 @@ ProcRemove(int pid) SHMEM_OFFSET location; PROC *proc; - location = INVALID_OFFSET; - location = ShmemPIDDestroy(pid); if (location == INVALID_OFFSET) return FALSE; @@ -398,43 +453,30 @@ ProcRemove(int pid) /* * 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; } /* @@ -476,69 +518,14 @@ ProcQueueInit(PROC_QUEUE *queue) } -/* - * 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). * @@ -629,7 +616,7 @@ ProcSleep(LOCKMETHODCTL *lockctl, ins:; /* ------------------- - * assume that these two operations are atomic (because + * Insert self into queue. These operations are atomic (because * of the spinlock). * ------------------- */ @@ -640,6 +627,18 @@ ins:; 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); /* -------------- @@ -667,8 +666,6 @@ ins:; 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 @@ -676,19 +673,22 @@ ins:; * 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 @@ -696,9 +696,16 @@ ins:; 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); @@ -836,20 +843,24 @@ ProcAddLock(SHM_QUEUE *elem) /* -------------------- * 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(); /* --------------------- @@ -869,7 +880,6 @@ HandleDeadLock(SIGNAL_ARGS) { UnlockLockTable(); errno = save_errno; - SetWaitingForLock(isWaitingForLock); /* restore waiting status */ return; } @@ -883,22 +893,23 @@ HandleDeadLock(SIGNAL_ARGS) /* 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. @@ -906,17 +917,16 @@ HandleDeadLock(SIGNAL_ARGS) */ 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; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 1a0aa5d0cd..ff2c0c283d 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * 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 @@ -84,9 +84,6 @@ bool Log_connections = false; CommandDest whereToSendOutput = Debug; - -extern void HandleDeadLock(SIGNAL_ARGS); - static bool dontExecute = false; /* note: these declarations had better match tcopprot.h */ @@ -94,7 +91,6 @@ DLLIMPORT sigjmp_buf Warn_restart; bool Warn_restart_ready = false; bool InError = false; -volatile bool ProcDiePending = false; static bool EchoQuery = false; /* default don't echo */ char pg_pathname[MAXPGPATH]; @@ -732,8 +728,7 @@ pg_exec_query_string(char *query_string, /* string to execute */ } /* 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. @@ -766,8 +761,7 @@ pg_exec_query_string(char *query_string, /* string to execute */ } /* If we got a cancel signal in analysis or prior command, quit */ - if (QueryCancel) - CancelQuery(); + CHECK_FOR_INTERRUPTS(); if (querytree->commandType == CMD_UTILITY) { @@ -793,8 +787,7 @@ pg_exec_query_string(char *query_string, /* string to execute */ 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(); @@ -898,40 +891,15 @@ finish_xact_command(void) /* -------------------------------- * 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) { @@ -943,88 +911,69 @@ 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) @@ -1034,6 +983,7 @@ 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) { @@ -1041,6 +991,36 @@ 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) @@ -1502,9 +1482,9 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha */ 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); /* @@ -1517,10 +1497,15 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha 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(); @@ -1683,7 +1668,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha 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"); } /* @@ -1714,6 +1699,16 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha * 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 @@ -1738,6 +1733,12 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha * 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) */ @@ -1770,27 +1771,34 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha /* ---------------- * (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(); /* ---------------- diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index b47a628cb1..460a7175ce 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -120,8 +120,10 @@ elog(int lev, const char *fmt, ...) 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; @@ -145,17 +147,16 @@ elog(int lev, const char *fmt, ...) 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) @@ -366,8 +367,6 @@ elog(int lev, const char *fmt, ...) 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 */ @@ -424,8 +423,6 @@ elog(int lev, const char *fmt, ...) fputs(msg_buf, stderr); } -#endif /* !PG_STANDALONE */ - /* done with the message, release space */ if (fmt_buf != fmt_fixedbuf) free(fmt_buf); @@ -437,6 +434,8 @@ elog(int lev, const char *fmt, ...) */ 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. @@ -477,7 +476,6 @@ elog(int lev, const char *fmt, ...) if (lev > FATAL) { - /* * Serious crash time. Postmaster will observe nonzero process * exit status and kill the other backends too. @@ -485,6 +483,7 @@ elog(int lev, const char *fmt, ...) * XXX: what if we are *in* the postmaster? proc_exit() won't kill * our children... */ + ImmediateInterruptOK = false; fflush(stdout); fflush(stderr); proc_exit(lev); @@ -493,8 +492,6 @@ elog(int lev, const char *fmt, ...) /* We reach here if lev <= NOTICE. OK to return to caller. */ } -#ifndef PG_STANDALONE - int DebugFileOpen(void) { @@ -556,9 +553,6 @@ DebugFileOpen(void) return Debugfile; } -#endif - - /* * Return a timestamp string like @@ -602,6 +596,10 @@ print_pid(void) #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. * @@ -613,10 +611,6 @@ print_pid(void) 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; diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 4359ef60a8..8e9d4c95db 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -8,7 +8,7 @@ * * * 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 @@ -34,7 +34,12 @@ ProtocolVersion FrontendProtocol = PG_PROTOCOL_LATEST; 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; @@ -56,9 +61,7 @@ BackendId MyBackendId; char *DatabaseName = NULL; char *DatabasePath = NULL; -bool MyDatabaseIdIsInitialized = false; Oid MyDatabaseId = InvalidOid; -bool TransactionInitWasProcessed = false; bool IsUnderPostmaster = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 7736ec92e8..47e14c3a82 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -3,7 +3,7 @@ * * 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 @@ -101,7 +101,6 @@ typedef XLogPageHeaderData *XLogPageHeader; extern StartUpID ThisStartUpID; /* current SUI */ extern bool InRecovery; extern XLogRecPtr MyLastRecPtr; -extern volatile uint32 CritSectionCount; typedef struct RmgrData { diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 6b5dff9478..39410abe13 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -12,10 +12,10 @@ * 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. * *------------------------------------------------------------------------- @@ -23,11 +23,63 @@ #ifndef MISCADMIN_H #define MISCADMIN_H -#include /* 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 -- * *****************************************************************************/ @@ -42,7 +94,6 @@ extern int PostmasterMain(int argc, char *argv[]); */ extern bool Noversion; extern bool Quiet; -extern volatile bool QueryCancel; extern char *DataDir; extern int MyProcPid; @@ -56,9 +107,7 @@ extern char OutputFileName[]; * * extern BackendId MyBackendId; */ -extern bool MyDatabaseIdIsInitialized; extern Oid MyDatabaseId; -extern bool TransactionInitWasProcessed; extern bool IsUnderPostmaster; @@ -143,7 +192,8 @@ extern void SetSessionUserIdFromUserName(const char *username); 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 @@ -157,17 +207,17 @@ extern char *convertstr(unsigned char *buff, int len, int dest); /* * 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. @@ -185,27 +235,13 @@ typedef enum ProcessingMode * 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; @@ -215,21 +251,22 @@ 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 */ diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h index 55aa296358..ca8f827d4f 100644 --- a/src/include/storage/ipc.h +++ b/src/include/storage/ipc.h @@ -7,7 +7,7 @@ * 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 @@ -99,7 +99,7 @@ extern IpcSemaphoreId IpcSemaphoreCreate(int numSems, int permission, 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); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 96928eb452..65308d71ad 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -22,9 +22,8 @@ extern int DeadlockTimeout; typedef struct { - int sleeplock; - IpcSemaphoreId semId; - int semNum; + IpcSemaphoreId semId; /* SysV semaphore set ID */ + int semNum; /* semaphore number within set */ } SEMA; /* @@ -38,12 +37,6 @@ struct proc 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 */ @@ -72,6 +65,9 @@ struct proc extern PROC *MyProc; +extern SPINLOCK ProcStructLock; + + #define PROC_INCR_SLOCK(lock) \ do { \ if (MyProc) (MyProc->sLocks[(lock)])++; \ @@ -89,11 +85,6 @@ do { \ #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. @@ -142,5 +133,6 @@ extern int ProcLockWakeup(LOCKMETHOD lockmethod, LOCK *lock); extern void ProcAddLock(SHM_QUEUE *elem); extern void ProcReleaseSpins(PROC *proc); extern void LockWaitCancel(void); +extern void HandleDeadLock(SIGNAL_ARGS); #endif /* PROC_H */ diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 0b97397d7c..d7b7ff7685 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -7,7 +7,7 @@ * 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 @@ -40,9 +40,7 @@ extern void pg_exec_query_string(char *query_string, #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); diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index da9178b276..8468118177 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -7,13 +7,14 @@ * 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 */ @@ -23,47 +24,25 @@ #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 */ diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 7fe527293f..1bfb406ac5 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -12,10 +12,12 @@ * * * 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 #include #include