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.c258
1 files changed, 106 insertions, 152 deletions
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 2da1495cd3b..0193a7ad2e7 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.83 2000/10/07 14:39:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.84 2000/11/28 23:27:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,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.83 2000/10/07 14:39:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.84 2000/11/28 23:27:56 tgl Exp $
*/
#include "postgres.h"
@@ -91,10 +91,8 @@ static PROC_HDR *ProcGlobal = NULL;
PROC *MyProc = NULL;
static void ProcKill(int exitStatus, Datum pid);
-static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);
-static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);
-
-static char *DeadLockMessage = "Deadlock detected -- See the lock(l) manual page for a possible cause.";
+static void ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum);
+static void ProcFreeSem(IpcSemaphoreId semId, int semNum);
/*
* InitProcGlobal -
@@ -116,7 +114,7 @@ static char *DeadLockMessage = "Deadlock detected -- See the lock(l) manual page
* rather than later.
*/
void
-InitProcGlobal(IPCKey key, int maxBackends)
+InitProcGlobal(int maxBackends)
{
bool found = false;
@@ -135,39 +133,35 @@ InitProcGlobal(IPCKey key, int maxBackends)
int i;
ProcGlobal->freeProcs = INVALID_OFFSET;
- ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
- for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
+ {
+ ProcGlobal->procSemIds[i] = -1;
ProcGlobal->freeSemMap[i] = 0;
+ }
/*
* Arrange to delete semas on exit --- set this up now so that we
- * will clean up if pre-allocation fails...
+ * will clean up if pre-allocation fails. We use our own freeproc,
+ * rather than IpcSemaphoreCreate's removeOnExit option, because
+ * we don't want to fill up the on_shmem_exit list with a separate
+ * entry for each semaphore set.
*/
on_shmem_exit(ProcFreeAllSemaphores, 0);
/*
- * Pre-create the semaphores for the first maxBackends processes,
- * unless we are running as a standalone backend.
+ * Pre-create the semaphores for the first maxBackends processes.
*/
- if (key != PrivateIPCKey)
+ Assert(maxBackends > 0 && maxBackends <= MAXBACKENDS);
+
+ for (i = 0; i < ((maxBackends-1)/PROC_NSEMS_PER_SET+1); i++)
{
- for (i = 0;
- i < (maxBackends + PROC_NSEMS_PER_SET - 1) / PROC_NSEMS_PER_SET;
- i++)
- {
- IPCKey semKey = ProcGlobal->currKey + i;
- int semId;
-
- semId = IpcSemaphoreCreate(semKey,
- PROC_NSEMS_PER_SET,
- IPCProtection,
- IpcSemaphoreDefaultStartValue,
- 0);
- if (semId < 0)
- elog(FATAL, "InitProcGlobal: IpcSemaphoreCreate failed");
- /* mark this sema set allocated */
- ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET);
- }
+ IpcSemaphoreId semId;
+
+ semId = IpcSemaphoreCreate(PROC_NSEMS_PER_SET,
+ IPCProtection,
+ 1,
+ false);
+ ProcGlobal->procSemIds[i] = semId;
}
}
}
@@ -178,7 +172,7 @@ InitProcGlobal(IPCKey key, int maxBackends)
* ------------------------
*/
void
-InitProcess(IPCKey key)
+InitProcess(void)
{
bool found = false;
unsigned long location,
@@ -186,7 +180,7 @@ InitProcess(IPCKey key)
SpinAcquire(ProcStructLock);
- /* attach to the free list */
+ /* attach to the ProcGlobal structure */
ProcGlobal = (PROC_HDR *)
ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
if (!found)
@@ -199,10 +193,9 @@ InitProcess(IPCKey key)
{
SpinRelease(ProcStructLock);
elog(ERROR, "ProcInit: you already exist");
- return;
}
- /* try to get a proc from the free list first */
+ /* try to get a proc struct from the free list first */
myOffset = ProcGlobal->freeProcs;
@@ -243,36 +236,22 @@ InitProcess(IPCKey key)
if (IsUnderPostmaster)
{
- IPCKey semKey;
- int semNum;
- int semId;
- union semun semun;
-
- ProcGetNewSemKeyAndNum(&semKey, &semNum);
+ IpcSemaphoreId semId;
+ int semNum;
+ union semun semun;
- /*
- * Note: because of the pre-allocation done in InitProcGlobal,
- * this call should always attach to an existing semaphore. It
- * will (try to) create a new group of semaphores only if the
- * postmaster tries to start more backends than it said it would.
- */
- semId = IpcSemaphoreCreate(semKey,
- PROC_NSEMS_PER_SET,
- IPCProtection,
- IpcSemaphoreDefaultStartValue,
- 0);
+ ProcGetNewSemIdAndNum(&semId, &semNum);
/*
* we might be reusing a semaphore that belongs to a dead backend.
* So be careful and reinitialize its value here.
*/
- semun.val = IpcSemaphoreDefaultStartValue;
+ semun.val = 1;
semctl(semId, semNum, SETVAL, semun);
- IpcSemaphoreLock(semId, semNum, IpcExclusiveLock);
+ IpcSemaphoreLock(semId, semNum);
MyProc->sem.semId = semId;
MyProc->sem.semNum = semNum;
- MyProc->sem.semKey = semKey;
}
else
MyProc->sem.semId = -1;
@@ -304,7 +283,7 @@ InitProcess(IPCKey key)
*/
location = MAKE_OFFSET(MyProc);
if ((!ShmemPIDLookup(MyProcPid, &location)) || (location != MAKE_OFFSET(MyProc)))
- elog(STOP, "InitProc: ShmemPID table broken");
+ elog(STOP, "InitProcess: ShmemPID table broken");
MyProc->errType = NO_ERROR;
SHMQueueElemInit(&(MyProc->links));
@@ -363,10 +342,7 @@ ProcReleaseLocks()
/*
* ProcRemove -
* used by the postmaster to clean up the global tables. This also frees
- * up the semaphore used for the lmgr of the process. (We have to do
- * this is the postmaster instead of doing a IpcSemaphoreKill on exiting
- * the process because the semaphore set is shared among backends and
- * we don't want to remove other's semaphores on exit.)
+ * up the semaphore used for the lmgr of the process.
*/
bool
ProcRemove(int pid)
@@ -383,7 +359,7 @@ ProcRemove(int pid)
SpinAcquire(ProcStructLock);
- ProcFreeSem(proc->sem.semKey, proc->sem.semNum);
+ ProcFreeSem(proc->sem.semId, proc->sem.semNum);
proc->links.next = ProcGlobal->freeProcs;
ProcGlobal->freeProcs = MAKE_OFFSET(proc);
@@ -490,6 +466,7 @@ ProcQueueInit(PROC_QUEUE *queue)
*
*/
static bool lockWaiting = false;
+
void
SetWaitingForLock(bool waiting)
{
@@ -514,12 +491,12 @@ SetWaitingForLock(bool waiting)
}
}
}
+
void
LockWaitCancel(void)
{
-/* BeOS doesn't have setitimer, but has set_alarm */
#ifndef __BEOS__
-struct itimerval timeval,
+ struct itimerval timeval,
dummy;
if (!lockWaiting)
@@ -529,6 +506,7 @@ struct itimerval timeval,
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;
@@ -547,6 +525,8 @@ struct itimerval timeval,
* semaphore is cleared by default, so the first time we try
* to acquire it, we sleep.
*
+ * Result is NO_ERROR 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.
*
@@ -566,7 +546,6 @@ ProcSleep(PROC_QUEUE *waitQueue,/* lock->waitProcs */
int aheadHolders[MAX_LOCKMODES];
bool selfConflict = (lockctl->conflictTab[token] & myMask),
prevSame = false;
- bool deadlock_checked = false;
#ifndef __BEOS__
struct itimerval timeval,
dummy;
@@ -595,8 +574,8 @@ ProcSleep(PROC_QUEUE *waitQueue,/* lock->waitProcs */
/* is he waiting for me ? */
if (lockctl->conflictTab[proc->token] & MyProc->holdLock)
{
+ /* Yes, report deadlock failure */
MyProc->errType = STATUS_ERROR;
- elog(NOTICE, DeadLockMessage);
goto rt;
}
/* being waiting for him - go past */
@@ -642,10 +621,16 @@ ins:;
lock->waitMask |= myMask;
SpinRelease(spinlock);
+ MyProc->errType = NO_ERROR; /* initialize result for success */
+
/* --------------
- * We set this so we can wake up periodically and check for a deadlock.
- * If a deadlock is detected, the handler releases the processes
- * semaphore and aborts the current transaction.
+ * Set timer so we can wake up after awhile and check for a deadlock.
+ * If a deadlock is detected, the handler releases the process's
+ * semaphore and sets MyProc->errType = STATUS_ERROR, allowing us to
+ * know that we must report failure rather than success.
+ *
+ * By delaying the check until we've waited for a bit, we can avoid
+ * running the rather expensive deadlock-check code in most cases.
*
* Need to zero out struct to set the interval and the micro seconds fields
* to 0.
@@ -655,49 +640,42 @@ ins:;
MemSet(&timeval, 0, sizeof(struct itimerval));
timeval.it_value.tv_sec = DeadlockTimeout / 1000;
timeval.it_value.tv_usec = (DeadlockTimeout % 1000) * 1000;
+ if (setitimer(ITIMER_REAL, &timeval, &dummy))
+ elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#else
- /* usecs */
- time_interval = DeadlockTimeout * 1000000;
+ time_interval = DeadlockTimeout * 1000000; /* usecs */
+ if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
+ elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
#endif
SetWaitingForLock(true);
- do
- {
- MyProc->errType = NO_ERROR; /* reset flag after deadlock check */
- if (!deadlock_checked)
-#ifndef __BEOS__
- if (setitimer(ITIMER_REAL, &timeval, &dummy))
-#else
- if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0)
-#endif
- elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
- deadlock_checked = true;
-
- /* --------------
- * if someone wakes us between SpinRelease and IpcSemaphoreLock,
- * IpcSemaphoreLock will not block. The wakeup is "saved" by
- * the semaphore implementation.
- * --------------
- */
- IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum,
- IpcExclusiveLock);
- } while (MyProc->errType == STATUS_NOT_FOUND); /* sleep after deadlock
- * check */
+ /* --------------
+ * If someone wakes us between SpinRelease 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 continue to wait. There used to be a loop here, but it
+ * was useless code...
+ * --------------
+ */
+ IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum);
+
lockWaiting = false;
/* ---------------
- * We were awoken before a timeout - now disable the timer
+ * Disable the timer, if it's still running
* ---------------
*/
#ifndef __BEOS__
timeval.it_value.tv_sec = 0;
timeval.it_value.tv_usec = 0;
if (setitimer(ITIMER_REAL, &timeval, &dummy))
+ elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#else
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
+ elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
#endif
- elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
/* ----------------
* We were assumed to be in a critical section when we went
@@ -742,7 +720,7 @@ ProcWakeup(PROC *proc, int errType)
proc->errType = errType;
- IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
+ IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum);
return retProc;
}
@@ -855,27 +833,11 @@ HandleDeadLock(SIGNAL_ARGS)
* Before we are awoken the process releasing the lock grants it to
* us so we know that we don't have to wait anymore.
*
- * Damn these names are LONG! -mer
+ * We check by looking to see if we've been unlinked from the wait queue.
+ * This is quicker than checking our semaphore's state, since no kernel
+ * call is needed, and it is safe because we hold the locktable lock.
* ---------------------
*/
- if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) ==
- IpcSemaphoreDefaultStartValue)
- {
- UnlockLockTable();
- return;
- }
-
- /*
- * you would think this would be unnecessary, but...
- *
- * this also means we've been removed already. in some ports (e.g.,
- * sparc and aix) the semop(2) implementation is such that we can
- * actually end up in this handler after someone has removed us from
- * the queue and bopped the semaphore *but the test above fails to
- * detect the semaphore update* (presumably something weird having to
- * do with the order in which the semaphore wakeup signal and SIGALRM
- * get handled).
- */
if (MyProc->links.prev == INVALID_OFFSET ||
MyProc->links.next == INVALID_OFFSET)
{
@@ -888,19 +850,18 @@ HandleDeadLock(SIGNAL_ARGS)
DumpAllLocks();
#endif
- MyProc->errType = STATUS_NOT_FOUND;
if (!DeadLockCheck(MyProc, MyProc->waitLock))
{
+ /* No deadlock, so keep waiting */
UnlockLockTable();
return;
}
- mywaitlock = MyProc->waitLock;
-
/* ------------------------
* Get this process off the lock's wait queue
* ------------------------
*/
+ mywaitlock = MyProc->waitLock;
Assert(mywaitlock->waitProcs.size > 0);
lockWaiting = false;
--mywaitlock->waitProcs.size;
@@ -908,12 +869,10 @@ HandleDeadLock(SIGNAL_ARGS)
SHMQueueElemInit(&(MyProc->links));
/* ------------------
- * Unlock my semaphore so that the count is right for next time.
- * I was awoken by a signal, not by someone unlocking my semaphore.
+ * Unlock my semaphore so that the interrupted ProcSleep() call can finish.
* ------------------
*/
- IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum,
- IpcExclusiveLock);
+ IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum);
/* -------------
* Set MyProc->errType to STATUS_ERROR so that we abort after
@@ -928,9 +887,6 @@ HandleDeadLock(SIGNAL_ARGS)
* conditions. i don't claim to understand this...
*/
UnlockLockTable();
-
- elog(NOTICE, DeadLockMessage);
- return;
}
void
@@ -959,31 +915,32 @@ ProcReleaseSpins(PROC *proc)
*****************************************************************************/
/*
- * ProcGetNewSemKeyAndNum -
+ * ProcGetNewSemIdAndNum -
* scan the free semaphore bitmap and allocate a single semaphore from
- * a semaphore set. (If the semaphore set doesn't exist yet,
- * IpcSemaphoreCreate will create it. Otherwise, we use the existing
- * semaphore set.)
+ * a semaphore set.
*/
static void
-ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
+ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum)
{
int i;
+ IpcSemaphoreId *procSemIds = ProcGlobal->procSemIds;
int32 *freeSemMap = ProcGlobal->freeSemMap;
- int32 fullmask = (1 << (PROC_NSEMS_PER_SET + 1)) - 1;
+ 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.
*/
- for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
{
int mask = 1;
int j;
if (freeSemMap[i] == fullmask)
continue; /* this set is fully allocated */
+ if (procSemIds[i] < 0)
+ continue; /* this set hasn't been initialized */
for (j = 0; j < PROC_NSEMS_PER_SET; j++)
{
@@ -991,12 +948,11 @@ ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
{
/*
- * a free semaphore found. Mark it as allocated. Also set
- * the bit indicating whole set is allocated.
+ * a free semaphore found. Mark it as allocated.
*/
- freeSemMap[i] |= mask + (1 << PROC_NSEMS_PER_SET);
+ freeSemMap[i] |= mask;
- *key = ProcGlobal->currKey + i;
+ *semId = procSemIds[i];
*semNum = j;
return;
}
@@ -1005,7 +961,7 @@ ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
}
/* if we reach here, all the semaphores are in use. */
- elog(ERROR, "InitProc: cannot allocate a free semaphore");
+ elog(ERROR, "ProcGetNewSemIdAndNum: cannot allocate a free semaphore");
}
/*
@@ -1013,23 +969,22 @@ ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
* free up our semaphore in the semaphore set.
*/
static void
-ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
+ProcFreeSem(IpcSemaphoreId semId, int semNum)
{
- int mask;
+ int32 mask;
int i;
- int32 *freeSemMap = ProcGlobal->freeSemMap;
- i = semKey - ProcGlobal->currKey;
mask = ~(1 << semNum);
- freeSemMap[i] &= mask;
- /*
- * Formerly we'd release a semaphore set if it was now completely
- * unused, but now we keep the semaphores to ensure we won't run out
- * when starting new backends --- cf. InitProcGlobal. Note that the
- * PROC_NSEMS_PER_SET+1'st bit of the freeSemMap entry remains set to
- * indicate it is still allocated; ProcFreeAllSemaphores() needs that.
- */
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
+ {
+ if (ProcGlobal->procSemIds[i] == semId)
+ {
+ ProcGlobal->freeSemMap[i] &= mask;
+ return;
+ }
+ }
+ fprintf(stderr, "ProcFreeSem: no ProcGlobal entry for semId %d\n", semId);
}
/*
@@ -1039,14 +994,13 @@ ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
* Free up all the semaphores allocated to the lmgrs of the backends.
*/
static void
-ProcFreeAllSemaphores()
+ProcFreeAllSemaphores(void)
{
int i;
- int32 *freeSemMap = ProcGlobal->freeSemMap;
- for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
+ for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
{
- if (freeSemMap[i] != 0)
- IpcSemaphoreKill(ProcGlobal->currKey + i);
+ if (ProcGlobal->procSemIds[i] >= 0)
+ IpcSemaphoreKill(ProcGlobal->procSemIds[i]);
}
}