aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/lmgr/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/lmgr/proc.c')
-rw-r--r--src/backend/storage/lmgr/proc.c270
1 files changed, 167 insertions, 103 deletions
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 0a02e6f006c..e687304dba7 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -8,15 +8,11 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.108 2001/09/21 17:06:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.109 2001/09/29 04:02:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
- * Each postgres backend gets one of these. We'll use it to
- * clean up after the process should the process suddenly die.
- *
- *
* Interface (a):
* ProcSleep(), ProcWakeup(),
* ProcQueueAlloc() -- create a shm queue for sleeping processes
@@ -75,27 +71,31 @@
#include "access/xact.h"
#include "storage/proc.h"
#include "storage/sinval.h"
+#include "storage/spin.h"
int DeadlockTimeout = 1000;
-/* --------------------
- * Spin lock for manipulating the shared process data structure:
- * ProcGlobal.... Adding an extra spin lock seemed like the smallest
- * hack to get around reading and updating this structure in shared
- * memory. -mer 17 July 1991
- * --------------------
+PROC *MyProc = NULL;
+
+/*
+ * This spinlock protects the freelist of recycled PROC structures and the
+ * bitmap of free semaphores. We cannot use an LWLock because the LWLock
+ * manager depends on already having a PROC and a wait semaphore! But these
+ * structures are touched relatively infrequently (only at backend startup
+ * or shutdown) and not for very long, so a spinlock is okay.
*/
-SPINLOCK ProcStructLock;
+static slock_t *ProcStructLock = NULL;
static PROC_HDR *ProcGlobal = NULL;
-PROC *MyProc = NULL;
+static PROC *DummyProc = NULL;
static bool waitingForLock = false;
static bool waitingForSignal = false;
static void ProcKill(void);
+static void DummyProcKill(void);
static void ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum);
static void ProcFreeSem(IpcSemaphoreId semId, int semNum);
static void ZeroProcSemaphore(PROC *proc);
@@ -128,9 +128,12 @@ InitProcGlobal(int maxBackends)
Size procGlobalSize;
bool found = false;
- /* Compute size for ProcGlobal structure */
+ /*
+ * Compute size for ProcGlobal structure. Note we need one more sema
+ * besides those used for regular backends.
+ */
Assert(maxBackends > 0);
- semMapEntries = PROC_SEM_MAP_ENTRIES(maxBackends);
+ semMapEntries = PROC_SEM_MAP_ENTRIES(maxBackends+1);
procGlobalSize = sizeof(PROC_HDR) + (semMapEntries-1) * sizeof(SEM_MAP_ENTRY);
/* Create or attach to the ProcGlobal shared structure */
@@ -178,13 +181,26 @@ InitProcGlobal(int maxBackends)
false);
ProcGlobal->procSemMap[i].procSemId = semId;
}
+
+ /*
+ * Pre-allocate a PROC structure for dummy (checkpoint) processes,
+ * and reserve the last sema of the precreated semas for it.
+ */
+ DummyProc = (PROC *) ShmemAlloc(sizeof(PROC));
+ DummyProc->pid = 0; /* marks DummyProc as not in use */
+ i = semMapEntries-1;
+ ProcGlobal->procSemMap[i].freeSemMap |= 1 << (PROC_NSEMS_PER_SET-1);
+ DummyProc->sem.semId = ProcGlobal->procSemMap[i].procSemId;
+ DummyProc->sem.semNum = PROC_NSEMS_PER_SET-1;
+
+ /* Create ProcStructLock spinlock, too */
+ ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
+ SpinLockInit(ProcStructLock);
}
}
-/* ------------------------
- * InitProc -- create a per-process data structure for this process
- * used by the lock manager on semaphore queues.
- * ------------------------
+/*
+ * InitProcess -- create a per-process data structure for this backend
*/
void
InitProcess(void)
@@ -202,39 +218,27 @@ InitProcess(void)
elog(ERROR, "InitProcess: you already exist");
/*
- * ProcStructLock protects the freelist of PROC entries and the map
- * of free semaphores. Note that when we acquire it here, we do not
- * have a PROC entry and so the ownership of the spinlock is not
- * recorded anywhere; even if it was, until we register ProcKill as
- * an on_shmem_exit callback, there is no exit hook that will cause
- * owned spinlocks to be released. Upshot: during the first part of
- * this routine, be careful to release the lock manually before any
- * elog(), else you'll have a stuck spinlock to add to your woes.
+ * try to get a proc struct from the free list first
*/
- SpinAcquire(ProcStructLock);
+ SpinLockAcquire(ProcStructLock);
- /* try to get a proc struct from the free list first */
myOffset = ProcGlobal->freeProcs;
if (myOffset != INVALID_OFFSET)
{
MyProc = (PROC *) MAKE_PTR(myOffset);
ProcGlobal->freeProcs = MyProc->links.next;
+ SpinLockRelease(ProcStructLock);
}
else
{
/*
- * have to allocate one. We can't use the normal shmem index
- * table mechanism because the proc structure is stored by PID
- * instead of by a global name (need to look it up by PID when we
- * cleanup dead processes).
+ * have to allocate a new one.
*/
+ SpinLockRelease(ProcStructLock);
MyProc = (PROC *) ShmemAlloc(sizeof(PROC));
if (!MyProc)
- {
- SpinRelease(ProcStructLock);
elog(FATAL, "cannot create new proc: out of memory");
- }
}
/*
@@ -246,39 +250,30 @@ InitProcess(void)
MyProc->errType = STATUS_OK;
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
+ MyProc->pid = MyProcPid;
+ MyProc->databaseId = MyDatabaseId;
MyProc->logRec.xrecoff = 0;
+ MyProc->lwWaiting = false;
+ MyProc->lwExclusive = false;
+ MyProc->lwWaitLink = NULL;
MyProc->waitLock = NULL;
MyProc->waitHolder = NULL;
- MyProc->pid = MyProcPid;
- MyProc->databaseId = MyDatabaseId;
SHMQueueInit(&(MyProc->procHolders));
- /*
- * Zero out the spin lock counts and set the sLocks field for
- * ProcStructLock to 1 as we have acquired this spinlock above but
- * didn't record it since we didn't have MyProc until now.
- */
- MemSet(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
- MyProc->sLocks[ProcStructLock] = 1;
/*
- * Arrange to clean up at backend exit. Once we do this, owned
- * spinlocks will be released on exit, and so we can be a lot less
- * tense about errors.
+ * Arrange to clean up at backend exit.
*/
on_shmem_exit(ProcKill, 0);
/*
* Set up a wait-semaphore for the proc. (We rely on ProcKill to clean
- * up if this fails.)
+ * up MyProc if this fails.)
*/
if (IsUnderPostmaster)
ProcGetNewSemIdAndNum(&MyProc->sem.semId, &MyProc->sem.semNum);
- /* Done with freelist and sem map */
- SpinRelease(ProcStructLock);
-
/*
- * We might be reusing a semaphore that belongs to a dead backend.
+ * We might be reusing a semaphore that belonged to a failed process.
* So be careful and reinitialize its value here.
*/
if (MyProc->sem.semId >= 0)
@@ -292,6 +287,65 @@ InitProcess(void)
}
/*
+ * InitDummyProcess -- create a dummy per-process data structure
+ *
+ * This is called by checkpoint processes so that they will have a MyProc
+ * value that's real enough to let them wait for LWLocks. The PROC and
+ * sema that are assigned are the extra ones created during InitProcGlobal.
+ */
+void
+InitDummyProcess(void)
+{
+ /*
+ * ProcGlobal should be set by a previous call to InitProcGlobal
+ * (we inherit this by fork() from the postmaster).
+ */
+ if (ProcGlobal == NULL || DummyProc == NULL)
+ elog(STOP, "InitDummyProcess: Proc Header uninitialized");
+
+ if (MyProc != NULL)
+ elog(ERROR, "InitDummyProcess: you already exist");
+
+ /*
+ * DummyProc should not presently be in use by anyone else
+ */
+ if (DummyProc->pid != 0)
+ elog(FATAL, "InitDummyProcess: DummyProc is in use by PID %d",
+ DummyProc->pid);
+ MyProc = DummyProc;
+
+ /*
+ * Initialize all fields of MyProc, except MyProc->sem which was
+ * set up by InitProcGlobal.
+ */
+ MyProc->pid = MyProcPid; /* marks DummyProc as in use by me */
+ SHMQueueElemInit(&(MyProc->links));
+ MyProc->errType = STATUS_OK;
+ MyProc->xid = InvalidTransactionId;
+ MyProc->xmin = InvalidTransactionId;
+ MyProc->databaseId = MyDatabaseId;
+ MyProc->logRec.xrecoff = 0;
+ MyProc->lwWaiting = false;
+ MyProc->lwExclusive = false;
+ MyProc->lwWaitLink = NULL;
+ MyProc->waitLock = NULL;
+ MyProc->waitHolder = NULL;
+ SHMQueueInit(&(MyProc->procHolders));
+
+ /*
+ * Arrange to clean up at process exit.
+ */
+ on_shmem_exit(DummyProcKill, 0);
+
+ /*
+ * We might be reusing a semaphore that belonged to a failed process.
+ * So be careful and reinitialize its value here.
+ */
+ if (MyProc->sem.semId >= 0)
+ ZeroProcSemaphore(MyProc);
+}
+
+/*
* Initialize the proc's wait-semaphore to count zero.
*/
static void
@@ -330,10 +384,10 @@ LockWaitCancel(void)
disable_sigalrm_interrupt();
/* Unlink myself from the wait queue, if on it (might not be anymore!) */
- LockLockTable();
+ LWLockAcquire(LockMgrLock, LW_EXCLUSIVE);
if (MyProc->links.next != INVALID_OFFSET)
RemoveFromWaitQueue(MyProc);
- UnlockLockTable();
+ LWLockRelease(LockMgrLock);
/*
* Reset the proc wait semaphore to zero. This is necessary in the
@@ -381,15 +435,18 @@ ProcReleaseLocks(bool isCommit)
/*
* ProcKill() -- Destroy the per-proc data structure for
- * this process. Release any of its held spin locks.
+ * this process. Release any of its held LW locks.
*/
static void
ProcKill(void)
{
Assert(MyProc != NULL);
- /* Release any spinlocks I am holding */
- ProcReleaseSpins(MyProc);
+ /* Release any LW locks I am holding */
+ LWLockReleaseAll();
+
+ /* Abort any buffer I/O in progress */
+ AbortBufferIO();
/* Get off any wait queue I might be on */
LockWaitCancel();
@@ -402,7 +459,7 @@ ProcKill(void)
LockReleaseAll(USER_LOCKMETHOD, MyProc, true, InvalidTransactionId);
#endif
- SpinAcquire(ProcStructLock);
+ SpinLockAcquire(ProcStructLock);
/* Free up my wait semaphore, if I got one */
if (MyProc->sem.semId >= 0)
@@ -412,10 +469,35 @@ ProcKill(void)
MyProc->links.next = ProcGlobal->freeProcs;
ProcGlobal->freeProcs = MAKE_OFFSET(MyProc);
- /* PROC struct isn't mine anymore; stop tracking spinlocks with it! */
+ /* PROC struct isn't mine anymore */
MyProc = NULL;
- SpinRelease(ProcStructLock);
+ SpinLockRelease(ProcStructLock);
+}
+
+/*
+ * DummyProcKill() -- Cut-down version of ProcKill for dummy (checkpoint)
+ * processes. The PROC and sema are not released, only marked
+ * as not-in-use.
+ */
+static void
+DummyProcKill(void)
+{
+ Assert(MyProc != NULL && MyProc == DummyProc);
+
+ /* Release any LW locks I am holding */
+ LWLockReleaseAll();
+
+ /* Abort any buffer I/O in progress */
+ AbortBufferIO();
+
+ /* I can't be on regular lock queues, so needn't check */
+
+ /* Mark DummyProc no longer in use */
+ MyProc->pid = 0;
+
+ /* PROC struct isn't mine anymore */
+ MyProc = NULL;
}
@@ -464,13 +546,13 @@ ProcQueueInit(PROC_QUEUE *queue)
* Caller must have set MyProc->heldLocks to reflect locks already held
* on the lockable object by this process (under all XIDs).
*
- * Locktable's spinlock must be held at entry, and will be held
+ * Locktable's masterLock must be held at entry, and will be held
* at exit.
*
* Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock).
*
* ASSUME: that no one will fiddle with the queue until after
- * we release the spin lock.
+ * we release the masterLock.
*
* NOTES: The process queue is now a priority queue for locking.
*
@@ -484,7 +566,7 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
HOLDER *holder)
{
LOCKMETHODCTL *lockctl = lockMethodTable->ctl;
- SPINLOCK spinlock = lockctl->masterLock;
+ LWLockId masterLock = lockctl->masterLock;
PROC_QUEUE *waitQueue = &(lock->waitProcs);
int myHeldLocks = MyProc->heldLocks;
bool early_deadlock = false;
@@ -595,14 +677,14 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
waitingForLock = true;
/*
- * Release the locktable's spin lock.
+ * Release the locktable's masterLock.
*
* 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);
+ LWLockRelease(masterLock);
/*
* Set timer so we can wake up after awhile and check for a deadlock.
@@ -617,7 +699,7 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
/*
- * If someone wakes us between SpinRelease and IpcSemaphoreLock,
+ * If someone wakes us between LWLockRelease and IpcSemaphoreLock,
* IpcSemaphoreLock will not block. The wakeup is "saved" by the
* semaphore implementation. Note also that if HandleDeadLock is
* invoked but does not detect a deadlock, IpcSemaphoreLock() will
@@ -644,12 +726,9 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
waitingForLock = false;
/*
- * 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.
+ * Re-acquire the locktable's masterLock.
*/
- SpinAcquire(spinlock);
+ LWLockAcquire(masterLock, LW_EXCLUSIVE);
/*
* We don't have to do anything else, because the awaker did all the
@@ -674,7 +753,7 @@ ProcWakeup(PROC *proc, int errType)
{
PROC *retProc;
- /* assume that spinlock has been acquired */
+ /* assume that masterLock has been acquired */
/* Proc should be sleeping ... */
if (proc->links.prev == INVALID_OFFSET ||
@@ -777,11 +856,11 @@ HandleDeadLock(SIGNAL_ARGS)
/*
* 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
+ * locktable lock, else this will wait forever. Also note that
+ * LWLockAcquire creates a critical section, so that this
* routine cannot be interrupted by cancel/die interrupts.
*/
- LockLockTable();
+ LWLockAcquire(LockMgrLock, LW_EXCLUSIVE);
/*
* Check to see if we've been awoken by anyone in the interim.
@@ -799,7 +878,7 @@ HandleDeadLock(SIGNAL_ARGS)
if (MyProc->links.prev == INVALID_OFFSET ||
MyProc->links.next == INVALID_OFFSET)
{
- UnlockLockTable();
+ LWLockRelease(LockMgrLock);
errno = save_errno;
return;
}
@@ -812,7 +891,7 @@ HandleDeadLock(SIGNAL_ARGS)
if (!DeadLockCheck(MyProc))
{
/* No deadlock, so keep waiting */
- UnlockLockTable();
+ LWLockRelease(LockMgrLock);
errno = save_errno;
return;
}
@@ -846,30 +925,10 @@ HandleDeadLock(SIGNAL_ARGS)
* wakable because we're not in front of them anymore. However,
* RemoveFromWaitQueue took care of waking up any such processes.
*/
- UnlockLockTable();
+ LWLockRelease(LockMgrLock);
errno = save_errno;
}
-void
-ProcReleaseSpins(PROC *proc)
-{
- int i;
-
- if (!proc)
- proc = MyProc;
-
- if (!proc)
- return;
- for (i = 0; i < (int) MAX_SPINS; i++)
- {
- if (proc->sLocks[i])
- {
- Assert(proc->sLocks[i] == 1);
- SpinRelease(i);
- }
- }
- AbortBufferIO();
-}
/*
* ProcWaitForSignal - wait for a signal from another backend.
@@ -994,10 +1053,7 @@ ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum)
SEM_MAP_ENTRY *procSemMap = ProcGlobal->procSemMap;
int32 fullmask = (1 << PROC_NSEMS_PER_SET) - 1;
- /*
- * we hold ProcStructLock when entering this routine. We scan through
- * the bitmap to look for a free semaphore.
- */
+ SpinLockAcquire(ProcStructLock);
for (i = 0; i < semMapEntries; i++)
{
@@ -1018,12 +1074,17 @@ ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum)
*semId = procSemMap[i].procSemId;
*semNum = j;
+
+ SpinLockRelease(ProcStructLock);
+
return;
}
mask <<= 1;
}
}
+ SpinLockRelease(ProcStructLock);
+
/*
* If we reach here, all the semaphores are in use. This is one of the
* possible places to detect "too many backends", so give the standard
@@ -1036,6 +1097,8 @@ ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum)
/*
* ProcFreeSem -
* free up our semaphore in the semaphore set.
+ *
+ * Caller is assumed to hold ProcStructLock.
*/
static void
ProcFreeSem(IpcSemaphoreId semId, int semNum)
@@ -1054,6 +1117,7 @@ ProcFreeSem(IpcSemaphoreId semId, int semNum)
return;
}
}
+ /* can't elog here!!! */
fprintf(stderr, "ProcFreeSem: no ProcGlobal entry for semId %d\n", semId);
}