1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* proc.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* routines to manage per-process shared memory data structure
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2001-01-24 20:43:33 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.96 2001/01/24 19:43:08 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Each postgres backend gets one of these. We'll use it to
|
|
|
|
* clean up after the process should the process suddenly die.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* Interface (a):
|
1997-09-07 07:04:48 +02:00
|
|
|
* ProcSleep(), ProcWakeup(), ProcWakeupNext(),
|
|
|
|
* ProcQueueAlloc() -- create a shm queue for sleeping processes
|
|
|
|
* ProcQueueInit() -- create a queue without allocing memory
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Locking and waiting for buffers can cause the backend to be
|
|
|
|
* put to sleep. Whoever releases the lock, etc. wakes the
|
|
|
|
* process up again (and gives it an error code so it knows
|
|
|
|
* whether it was awoken on an error condition).
|
|
|
|
*
|
|
|
|
* Interface (b):
|
|
|
|
*
|
2000-12-18 01:44:50 +01:00
|
|
|
* ProcReleaseLocks -- frees the locks associated with current transaction
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* ProcKill -- destroys the shared memory state (and locks)
|
1997-09-07 07:04:48 +02:00
|
|
|
* associated with the process.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* 5/15/91 -- removed the buffer pool based lock chain in favor
|
1997-09-07 07:04:48 +02:00
|
|
|
* of a shared memory lock chain. The write-protection is
|
|
|
|
* more expensive if the lock chain is in the buffer pool.
|
|
|
|
* The only reason I kept the lock chain in the buffer pool
|
|
|
|
* in the first place was to allow the lock table to grow larger
|
|
|
|
* than available shared memory and that isn't going to work
|
|
|
|
* without a lot of unimplemented support anyway.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* 4/7/95 -- instead of allocating a set of 1 semaphore per process, we
|
1997-09-07 07:04:48 +02:00
|
|
|
* allocate a semaphore from a set of PROC_NSEMS_PER_SET semaphores
|
|
|
|
* shared among backends (we keep a few sets of semaphores around).
|
|
|
|
* This is so that we can support more backends. (system-wide semaphore
|
|
|
|
* sets run out pretty fast.) -ay 4/95
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.96 2001/01/24 19:43:08 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-10-02 21:42:56 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
2000-12-18 18:33:42 +01:00
|
|
|
#include <errno.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <unistd.h>
|
1996-11-27 08:17:48 +01:00
|
|
|
#include <signal.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <sys/types.h>
|
1996-11-03 06:08:01 +01:00
|
|
|
|
1999-10-12 16:54:28 +02:00
|
|
|
#if defined(solaris_sparc) || defined(__CYGWIN__)
|
1996-11-06 07:52:23 +01:00
|
|
|
#include <sys/ipc.h>
|
|
|
|
#include <sys/sem.h>
|
|
|
|
#endif
|
|
|
|
|
2000-12-11 17:35:59 +01:00
|
|
|
#include "miscadmin.h"
|
|
|
|
|
2000-12-11 01:49:54 +01:00
|
|
|
#if defined(__darwin__)
|
|
|
|
#include "port/darwin/sem.h"
|
|
|
|
#endif
|
|
|
|
|
1999-12-16 02:25:23 +01:00
|
|
|
/* In Ultrix and QNX, sem.h must be included after ipc.h */
|
2000-10-03 05:11:26 +02:00
|
|
|
#ifdef HAVE_SYS_SEM_H
|
1997-01-08 09:33:07 +01:00
|
|
|
#include <sys/sem.h>
|
2000-10-03 05:11:26 +02:00
|
|
|
#endif
|
1999-07-16 07:00:38 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
#include "access/xact.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "storage/proc.h"
|
|
|
|
|
2000-12-11 01:49:54 +01:00
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
int DeadlockTimeout = 1000;
|
1998-08-25 23:20:32 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* --------------------
|
|
|
|
* 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
|
|
|
|
* --------------------
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
SPINLOCK ProcStructLock;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
static PROC_HDR *ProcGlobal = NULL;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
PROC *MyProc = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
static bool waitingForLock = false;
|
|
|
|
|
|
|
|
static void ProcKill(void);
|
2000-11-29 00:27:57 +01:00
|
|
|
static void ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum);
|
|
|
|
static void ProcFreeSem(IpcSemaphoreId semId, int semNum);
|
2001-01-14 06:08:17 +01:00
|
|
|
static void ZeroProcSemaphore(PROC *proc);
|
|
|
|
static void ProcFreeAllSemaphores(void);
|
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* InitProcGlobal -
|
1997-09-07 07:04:48 +02:00
|
|
|
* initializes the global process table. We put it here so that
|
1999-02-19 07:06:39 +01:00
|
|
|
* the postmaster can do this initialization. (ProcFreeAllSemaphores needs
|
1997-09-07 07:04:48 +02:00
|
|
|
* to read this table on exiting the postmaster. If we have the first
|
|
|
|
* backend do this, starting up and killing the postmaster without
|
|
|
|
* starting any backends will be a problem.)
|
1999-02-19 07:06:39 +01:00
|
|
|
*
|
|
|
|
* We also allocate all the per-process semaphores we will need to support
|
|
|
|
* the requested number of backends. We used to allocate semaphores
|
|
|
|
* only when backends were actually started up, but that is bad because
|
|
|
|
* it lets Postgres fail under load --- a lot of Unix systems are
|
|
|
|
* (mis)configured with small limits on the number of semaphores, and
|
|
|
|
* running out when trying to start another backend is a common failure.
|
|
|
|
* So, now we grab enough semaphores to support the desired max number
|
|
|
|
* of backends immediately at initialization --- if the sysadmin has set
|
|
|
|
* MaxBackends higher than his kernel will support, he'll find out sooner
|
|
|
|
* rather than later.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2000-11-29 00:27:57 +01:00
|
|
|
InitProcGlobal(int maxBackends)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
bool found = false;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* attach to the free list */
|
|
|
|
ProcGlobal = (PROC_HDR *)
|
2000-06-28 05:33:33 +02:00
|
|
|
ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* --------------------
|
|
|
|
* We're the first - initialize.
|
1999-02-19 08:10:48 +01:00
|
|
|
* XXX if found should ever be true, it is a sign of impending doom ...
|
|
|
|
* ought to complain if so?
|
1997-09-07 07:04:48 +02:00
|
|
|
* --------------------
|
|
|
|
*/
|
|
|
|
if (!found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
ProcGlobal->freeProcs = INVALID_OFFSET;
|
2000-11-29 00:27:57 +01:00
|
|
|
for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
|
|
|
|
{
|
|
|
|
ProcGlobal->procSemIds[i] = -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
ProcGlobal->freeSemMap[i] = 0;
|
2000-11-29 00:27:57 +01:00
|
|
|
}
|
1999-02-19 08:10:48 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
|
|
|
* Arrange to delete semas on exit --- set this up now so that we
|
2000-11-29 00:27:57 +01:00
|
|
|
* 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.
|
1999-02-19 08:10:48 +01:00
|
|
|
*/
|
2000-10-02 21:42:56 +02:00
|
|
|
on_shmem_exit(ProcFreeAllSemaphores, 0);
|
1999-02-19 08:10:48 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
2000-11-29 00:27:57 +01:00
|
|
|
* Pre-create the semaphores for the first maxBackends processes.
|
1999-02-21 02:41:55 +01:00
|
|
|
*/
|
2000-11-29 00:27:57 +01:00
|
|
|
Assert(maxBackends > 0 && maxBackends <= MAXBACKENDS);
|
|
|
|
|
|
|
|
for (i = 0; i < ((maxBackends-1)/PROC_NSEMS_PER_SET+1); i++)
|
1999-02-19 07:06:39 +01:00
|
|
|
{
|
2000-11-29 00:27:57 +01:00
|
|
|
IpcSemaphoreId semId;
|
|
|
|
|
|
|
|
semId = IpcSemaphoreCreate(PROC_NSEMS_PER_SET,
|
|
|
|
IPCProtection,
|
|
|
|
1,
|
|
|
|
false);
|
|
|
|
ProcGlobal->procSemIds[i] = semId;
|
1999-02-19 07:06:39 +01:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------
|
|
|
|
* InitProc -- create a per-process data structure for this process
|
|
|
|
* used by the lock manager on semaphore queues.
|
|
|
|
* ------------------------
|
|
|
|
*/
|
|
|
|
void
|
2000-11-29 00:27:57 +01:00
|
|
|
InitProcess(void)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
bool found = false;
|
|
|
|
unsigned long location,
|
|
|
|
myOffset;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
SpinAcquire(ProcStructLock);
|
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
/* attach to the ProcGlobal structure */
|
1997-09-07 07:04:48 +02:00
|
|
|
ProcGlobal = (PROC_HDR *)
|
2000-06-28 05:33:33 +02:00
|
|
|
ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!found)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* this should not happen. InitProcGlobal() is called before this. */
|
1999-10-06 23:58:18 +02:00
|
|
|
elog(STOP, "InitProcess: Proc Header uninitialized");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (MyProc != NULL)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(ProcStructLock);
|
1998-01-07 22:07:04 +01:00
|
|
|
elog(ERROR, "ProcInit: you already exist");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
/* try to get a proc struct from the free list first */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
myOffset = ProcGlobal->freeProcs;
|
|
|
|
|
|
|
|
if (myOffset != INVALID_OFFSET)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
MyProc = (PROC *) MAKE_PTR(myOffset);
|
|
|
|
ProcGlobal->freeProcs = MyProc->links.next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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).
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
|
2000-06-28 05:33:33 +02:00
|
|
|
MyProc = (PROC *) ShmemAlloc(sizeof(PROC));
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!MyProc)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SpinRelease(ProcStructLock);
|
|
|
|
elog(FATAL, "cannot create new proc: out of memory");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
|
1997-09-07 07:04:48 +02:00
|
|
|
MyProc->sLocks[ProcStructLock] = 1;
|
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/*
|
|
|
|
* Set up a wait-semaphore for the proc.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (IsUnderPostmaster)
|
|
|
|
{
|
2001-01-14 06:08:17 +01:00
|
|
|
ProcGetNewSemIdAndNum(&MyProc->sem.semId, &MyProc->sem.semNum);
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* we might be reusing a semaphore that belongs to a dead backend.
|
|
|
|
* So be careful and reinitialize its value here.
|
|
|
|
*/
|
2001-01-14 06:08:17 +01:00
|
|
|
ZeroProcSemaphore(MyProc);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2001-01-14 06:08:17 +01:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
MyProc->sem.semId = -1;
|
2001-01-14 06:08:17 +01:00
|
|
|
MyProc->sem.semNum = -1;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
SHMQueueElemInit(&(MyProc->links));
|
|
|
|
MyProc->errType = NO_ERROR;
|
1998-01-25 06:15:15 +01:00
|
|
|
MyProc->pid = MyProcPid;
|
1999-09-24 02:25:33 +02:00
|
|
|
MyProc->databaseId = MyDatabaseId;
|
1997-09-07 07:04:48 +02:00
|
|
|
MyProc->xid = InvalidTransactionId;
|
1998-07-27 21:38:40 +02:00
|
|
|
MyProc->xmin = InvalidTransactionId;
|
2001-01-22 23:30:06 +01:00
|
|
|
MyProc->waitLock = NULL;
|
|
|
|
MyProc->waitHolder = NULL;
|
|
|
|
SHMQueueInit(&(MyProc->procHolders));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-12 22:54:01 +01:00
|
|
|
/* ----------------------
|
|
|
|
* Release the lock.
|
|
|
|
* ----------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2001-01-12 22:54:01 +01:00
|
|
|
SpinRelease(ProcStructLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* -------------------------
|
1998-06-27 17:47:48 +02:00
|
|
|
* Install ourselves in the shmem index table. The name to
|
1997-09-07 07:04:48 +02:00
|
|
|
* use is determined by the OS-assigned process id. That
|
|
|
|
* allows the cleanup process to find us after any untimely
|
|
|
|
* exit.
|
|
|
|
* -------------------------
|
|
|
|
*/
|
|
|
|
location = MAKE_OFFSET(MyProc);
|
2001-01-14 06:08:17 +01:00
|
|
|
if ((!ShmemPIDLookup(MyProcPid, &location)) ||
|
|
|
|
(location != MAKE_OFFSET(MyProc)))
|
2000-11-29 00:27:57 +01:00
|
|
|
elog(STOP, "InitProcess: ShmemPID table broken");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
on_shmem_exit(ProcKill, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/*
|
|
|
|
* Remove a proc from the wait-queue it is on
|
|
|
|
* (caller must know it is on one).
|
|
|
|
* Locktable lock must be held by caller.
|
2000-12-22 01:51:54 +01:00
|
|
|
*
|
|
|
|
* NB: this does not remove the process' holder object, nor the lock object,
|
2001-01-16 07:11:34 +01:00
|
|
|
* even though their counts might now have gone to zero. That will happen
|
|
|
|
* during a subsequent LockReleaseAll call, which we expect will happen
|
2000-12-22 01:51:54 +01:00
|
|
|
* during transaction cleanup. (Removal of a proc from its wait queue by
|
|
|
|
* this routine can only happen if we are aborting the transaction.)
|
2000-02-21 03:42:37 +01:00
|
|
|
*/
|
2001-01-14 06:08:17 +01:00
|
|
|
static void
|
|
|
|
RemoveFromWaitQueue(PROC *proc)
|
2000-02-21 03:42:37 +01:00
|
|
|
{
|
2001-01-14 06:08:17 +01:00
|
|
|
LOCK *waitLock = proc->waitLock;
|
|
|
|
LOCKMODE lockmode = proc->waitLockMode;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/* 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--;
|
|
|
|
|
2001-01-16 07:11:34 +01:00
|
|
|
/* Undo increments of request counts by waiting process */
|
|
|
|
Assert(waitLock->nRequested > 0);
|
|
|
|
Assert(waitLock->nRequested > proc->waitLock->nGranted);
|
|
|
|
waitLock->nRequested--;
|
|
|
|
Assert(waitLock->requested[lockmode] > 0);
|
|
|
|
waitLock->requested[lockmode]--;
|
2001-01-14 06:08:17 +01:00
|
|
|
/* don't forget to clear waitMask bit if appropriate */
|
2001-01-16 07:11:34 +01:00
|
|
|
if (waitLock->granted[lockmode] == waitLock->requested[lockmode])
|
2001-01-14 06:08:17 +01:00
|
|
|
waitLock->waitMask &= ~(1 << lockmode);
|
|
|
|
|
|
|
|
/* Clean up the proc's own state */
|
|
|
|
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.
|
|
|
|
*
|
2001-01-16 21:59:34 +01:00
|
|
|
* Returns true if we had been waiting for a lock, else false.
|
|
|
|
*
|
2001-01-14 06:08:17 +01:00
|
|
|
* (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.)
|
|
|
|
*/
|
2001-01-16 21:59:34 +01:00
|
|
|
bool
|
2001-01-14 06:08:17 +01:00
|
|
|
LockWaitCancel(void)
|
|
|
|
{
|
|
|
|
/* Nothing to do if we weren't waiting for a lock */
|
|
|
|
if (!waitingForLock)
|
2001-01-16 21:59:34 +01:00
|
|
|
return false;
|
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
waitingForLock = false;
|
|
|
|
|
|
|
|
/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
|
|
|
|
#ifndef __BEOS__
|
2000-02-21 03:42:37 +01:00
|
|
|
{
|
2001-01-14 06:08:17 +01:00
|
|
|
struct itimerval timeval,
|
|
|
|
dummy;
|
|
|
|
|
|
|
|
MemSet(&timeval, 0, sizeof(struct itimerval));
|
|
|
|
setitimer(ITIMER_REAL, &timeval, &dummy);
|
2000-02-21 03:42:37 +01:00
|
|
|
}
|
2001-01-14 06:08:17 +01:00
|
|
|
#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);
|
2000-02-21 03:42:37 +01:00
|
|
|
UnlockLockTable();
|
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
2001-01-16 21:59:34 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true even if we were kicked off the lock before we were
|
|
|
|
* able to remove ourselves.
|
|
|
|
*/
|
|
|
|
return true;
|
2000-02-21 03:42:37 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* ProcReleaseLocks() -- release locks associated with current transaction
|
|
|
|
* at transaction commit or abort
|
|
|
|
*
|
|
|
|
* At commit, we release only locks tagged with the current transaction's XID,
|
|
|
|
* leaving those marked with XID 0 (ie, session locks) undisturbed. At abort,
|
|
|
|
* we release all locks including XID 0, because we need to clean up after
|
|
|
|
* a failure. This logic will need extension if we ever support nested
|
|
|
|
* transactions.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Note that user locks are not released in either case.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2000-12-22 01:51:54 +01:00
|
|
|
ProcReleaseLocks(bool isCommit)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!MyProc)
|
|
|
|
return;
|
2001-01-14 06:08:17 +01:00
|
|
|
/* If waiting, get off wait queue (should only be needed after error) */
|
|
|
|
LockWaitCancel();
|
|
|
|
/* Release locks */
|
2000-12-22 01:51:54 +01:00
|
|
|
LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc,
|
|
|
|
!isCommit, GetCurrentTransactionId());
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcRemove -
|
2001-01-14 06:08:17 +01:00
|
|
|
* called by the postmaster to clean up the global tables after a
|
|
|
|
* backend exits. This also frees up the proc's wait semaphore.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
ProcRemove(int pid)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
SHMEM_OFFSET location;
|
|
|
|
PROC *proc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
location = ShmemPIDDestroy(pid);
|
|
|
|
if (location == INVALID_OFFSET)
|
1998-09-01 05:29:17 +02:00
|
|
|
return FALSE;
|
1997-09-07 07:04:48 +02:00
|
|
|
proc = (PROC *) MAKE_PTR(location);
|
|
|
|
|
|
|
|
SpinAcquire(ProcStructLock);
|
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
ProcFreeSem(proc->sem.semId, proc->sem.semNum);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* Add PROC struct to freelist so space can be recycled in future */
|
1997-09-07 07:04:48 +02:00
|
|
|
proc->links.next = ProcGlobal->freeProcs;
|
|
|
|
ProcGlobal->freeProcs = MAKE_OFFSET(proc);
|
|
|
|
|
|
|
|
SpinRelease(ProcStructLock);
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return TRUE;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcKill() -- Destroy the per-proc data structure for
|
1997-09-07 07:04:48 +02:00
|
|
|
* this process. Release any of its held spin locks.
|
2001-01-14 06:08:17 +01:00
|
|
|
*
|
|
|
|
* This is done inside the backend process before it exits.
|
|
|
|
* ProcRemove, above, will be done by the postmaster afterwards.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
static void
|
2001-01-14 06:08:17 +01:00
|
|
|
ProcKill(void)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-01-14 06:08:17 +01:00
|
|
|
Assert(MyProc);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/* Release any spinlocks I am holding */
|
|
|
|
ProcReleaseSpins(MyProc);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/* Get off any wait queue I might be on */
|
|
|
|
LockWaitCancel();
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
/* Remove from the standard lock table */
|
2001-01-14 06:08:17 +01:00
|
|
|
LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, true, InvalidTransactionId);
|
1996-10-11 05:22:59 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
#ifdef USER_LOCKS
|
|
|
|
/* Remove from the user lock table */
|
2001-01-14 06:08:17 +01:00
|
|
|
LockReleaseAll(USER_LOCKMETHOD, MyProc, true, InvalidTransactionId);
|
2000-12-22 01:51:54 +01:00
|
|
|
#endif
|
2001-01-14 06:08:17 +01:00
|
|
|
|
|
|
|
MyProc = NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcQueue package: routines for putting processes to sleep
|
1997-09-07 07:04:48 +02:00
|
|
|
* and waking them up
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcQueueAlloc -- alloc/attach to a shared memory process queue
|
|
|
|
*
|
|
|
|
* Returns: a pointer to the queue or NULL
|
|
|
|
* Side Effects: Initializes the queue if we allocated one
|
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
#ifdef NOT_USED
|
1997-09-08 04:41:22 +02:00
|
|
|
PROC_QUEUE *
|
1996-07-09 08:22:35 +02:00
|
|
|
ProcQueueAlloc(char *name)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
bool found;
|
|
|
|
PROC_QUEUE *queue = (PROC_QUEUE *)
|
2000-06-28 05:33:33 +02:00
|
|
|
ShmemInitStruct(name, sizeof(PROC_QUEUE), &found);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!queue)
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!found)
|
|
|
|
ProcQueueInit(queue);
|
1998-09-01 05:29:17 +02:00
|
|
|
return queue;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-08-19 23:40:56 +02:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcQueueInit -- initialize a shared memory process queue
|
|
|
|
*/
|
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
ProcQueueInit(PROC_QUEUE *queue)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
SHMQueueInit(&(queue->links));
|
|
|
|
queue->size = 0;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcSleep -- put a process to sleep
|
|
|
|
*
|
|
|
|
* P() on the semaphore should put us to sleep. The process
|
2001-01-14 06:08:17 +01:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-11-29 00:27:57 +01:00
|
|
|
* Result is NO_ERROR if we acquired the lock, STATUS_ERROR if not (deadlock).
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* ASSUME: that no one will fiddle with the queue until after
|
1997-09-07 07:04:48 +02:00
|
|
|
* we release the spin lock.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES: The process queue is now a priority queue for locking.
|
|
|
|
*/
|
|
|
|
int
|
2000-12-22 01:51:54 +01:00
|
|
|
ProcSleep(LOCKMETHODCTL *lockctl,
|
|
|
|
LOCKMODE lockmode,
|
|
|
|
LOCK *lock,
|
|
|
|
HOLDER *holder)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
PROC_QUEUE *waitQueue = &(lock->waitProcs);
|
1999-05-07 03:23:11 +02:00
|
|
|
SPINLOCK spinlock = lockctl->masterLock;
|
2000-12-22 01:51:54 +01:00
|
|
|
int myMask = (1 << lockmode);
|
1999-05-07 03:23:11 +02:00
|
|
|
int waitMask = lock->waitMask;
|
2000-12-22 01:51:54 +01:00
|
|
|
PROC *proc;
|
|
|
|
int i;
|
2001-01-16 07:11:34 +01:00
|
|
|
int aheadGranted[MAX_LOCKMODES];
|
2000-12-22 01:51:54 +01:00
|
|
|
bool selfConflict = (lockctl->conflictTab[lockmode] & myMask),
|
1999-05-07 03:23:11 +02:00
|
|
|
prevSame = false;
|
2000-10-03 05:11:26 +02:00
|
|
|
#ifndef __BEOS__
|
1998-12-29 20:32:08 +01:00
|
|
|
struct itimerval timeval,
|
|
|
|
dummy;
|
2000-10-03 05:11:26 +02:00
|
|
|
#else
|
|
|
|
bigtime_t time_interval;
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
proc = (PROC *) MAKE_PTR(waitQueue->links.next);
|
1998-02-19 16:04:45 +01:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
/* if we don't conflict with any waiter - be first in queue */
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!(lockctl->conflictTab[lockmode] & waitMask))
|
1999-05-07 03:23:11 +02:00
|
|
|
goto ins;
|
1998-02-19 16:04:45 +01:00
|
|
|
|
2001-01-16 07:11:34 +01:00
|
|
|
/* otherwise, determine where we should go into the queue */
|
1999-05-07 03:23:11 +02:00
|
|
|
for (i = 1; i < MAX_LOCKMODES; i++)
|
2001-01-16 07:11:34 +01:00
|
|
|
aheadGranted[i] = lock->granted[i];
|
|
|
|
(aheadGranted[lockmode])++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
for (i = 0; i < waitQueue->size; i++)
|
|
|
|
{
|
2001-01-16 07:11:34 +01:00
|
|
|
LOCKMODE procWaitMode = proc->waitLockMode;
|
|
|
|
|
|
|
|
/* must I wait for him ? */
|
|
|
|
if (lockctl->conflictTab[lockmode] & proc->heldLocks)
|
1999-05-07 03:23:11 +02:00
|
|
|
{
|
|
|
|
/* is he waiting for me ? */
|
2001-01-16 07:11:34 +01:00
|
|
|
if (lockctl->conflictTab[procWaitMode] & MyProc->heldLocks)
|
1999-05-07 03:23:11 +02:00
|
|
|
{
|
2000-11-29 00:27:57 +01:00
|
|
|
/* Yes, report deadlock failure */
|
1999-05-07 03:23:11 +02:00
|
|
|
MyProc->errType = STATUS_ERROR;
|
2001-01-22 23:30:06 +01:00
|
|
|
return STATUS_ERROR;
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
2001-01-16 07:11:34 +01:00
|
|
|
/* I must go after him in queue - so continue loop */
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
2001-01-16 07:11:34 +01:00
|
|
|
/* if he waits for me, go before him in queue */
|
|
|
|
else if (lockctl->conflictTab[procWaitMode] & MyProc->heldLocks)
|
1999-05-07 03:23:11 +02:00
|
|
|
break;
|
|
|
|
/* if conflicting locks requested */
|
2001-01-16 07:11:34 +01:00
|
|
|
else if (lockctl->conflictTab[procWaitMode] & myMask)
|
1999-05-07 03:23:11 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* If I request non self-conflicting lock and there are others
|
2001-01-16 07:11:34 +01:00
|
|
|
* requesting the same lock just before this guy - stop here.
|
1999-05-07 03:23:11 +02:00
|
|
|
*/
|
|
|
|
if (!selfConflict && prevSame)
|
|
|
|
break;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
/*
|
2001-01-16 07:11:34 +01:00
|
|
|
* Last attempt to not move any further to the back of the queue:
|
|
|
|
* if we don't conflict with remaining waiters, stop here.
|
1999-05-07 03:23:11 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
else if (!(lockctl->conflictTab[lockmode] & waitMask))
|
1999-05-07 03:23:11 +02:00
|
|
|
break;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2001-01-16 07:11:34 +01:00
|
|
|
/* Move past this guy, and update state accordingly */
|
|
|
|
prevSame = (procWaitMode == lockmode);
|
|
|
|
(aheadGranted[procWaitMode])++;
|
|
|
|
if (aheadGranted[procWaitMode] == lock->requested[procWaitMode])
|
|
|
|
waitMask &= ~(1 << procWaitMode);
|
2001-01-22 23:30:06 +01:00
|
|
|
proc = (PROC *) MAKE_PTR(proc->links.next);
|
1999-05-07 03:23:11 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
ins:;
|
1997-09-07 07:04:48 +02:00
|
|
|
/* -------------------
|
2001-01-22 23:30:06 +01:00
|
|
|
* Insert self into queue, ahead of the given proc (or at tail of queue).
|
1997-09-07 07:04:48 +02:00
|
|
|
* -------------------
|
|
|
|
*/
|
2001-01-22 23:30:06 +01:00
|
|
|
SHMQueueInsertBefore(&(proc->links), &(MyProc->links));
|
1998-01-27 04:00:43 +01:00
|
|
|
waitQueue->size++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-07 03:23:11 +02:00
|
|
|
lock->waitMask |= myMask;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* Set up wait information in PROC object, too */
|
|
|
|
MyProc->waitLock = lock;
|
|
|
|
MyProc->waitHolder = holder;
|
|
|
|
MyProc->waitLockMode = lockmode;
|
|
|
|
/* We assume the caller set up MyProc->heldLocks */
|
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
MyProc->errType = NO_ERROR; /* initialize result for success */
|
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/* 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.
|
|
|
|
* -------------------
|
|
|
|
*/
|
2000-12-18 18:33:42 +01:00
|
|
|
SpinRelease(spinlock);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* --------------
|
2000-11-29 00:27:57 +01:00
|
|
|
* 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.
|
1998-12-29 20:32:08 +01:00
|
|
|
*
|
|
|
|
* Need to zero out struct to set the interval and the micro seconds fields
|
|
|
|
* to 0.
|
1997-09-07 07:04:48 +02:00
|
|
|
* --------------
|
|
|
|
*/
|
2000-10-03 05:11:26 +02:00
|
|
|
#ifndef __BEOS__
|
1998-12-29 20:32:08 +01:00
|
|
|
MemSet(&timeval, 0, sizeof(struct itimerval));
|
2000-05-31 02:28:42 +02:00
|
|
|
timeval.it_value.tv_sec = DeadlockTimeout / 1000;
|
|
|
|
timeval.it_value.tv_usec = (DeadlockTimeout % 1000) * 1000;
|
2000-11-29 00:27:57 +01:00
|
|
|
if (setitimer(ITIMER_REAL, &timeval, &dummy))
|
|
|
|
elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
|
2000-10-03 05:11:26 +02:00
|
|
|
#else
|
2000-11-29 00:27:57 +01:00
|
|
|
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");
|
2000-10-03 05:11:26 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
/* --------------
|
|
|
|
* 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...
|
2001-01-14 06:08:17 +01:00
|
|
|
*
|
|
|
|
* 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).
|
2000-11-29 00:27:57 +01:00
|
|
|
* --------------
|
|
|
|
*/
|
2001-01-14 06:08:17 +01:00
|
|
|
IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, true);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-12-29 20:32:08 +01:00
|
|
|
/* ---------------
|
2000-11-29 00:27:57 +01:00
|
|
|
* Disable the timer, if it's still running
|
1998-12-29 20:32:08 +01:00
|
|
|
* ---------------
|
|
|
|
*/
|
2000-10-03 05:11:26 +02:00
|
|
|
#ifndef __BEOS__
|
2001-01-14 06:08:17 +01:00
|
|
|
MemSet(&timeval, 0, sizeof(struct itimerval));
|
1998-12-29 20:32:08 +01:00
|
|
|
if (setitimer(ITIMER_REAL, &timeval, &dummy))
|
2000-11-29 00:27:57 +01:00
|
|
|
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
|
2000-10-03 05:11:26 +02:00
|
|
|
#else
|
|
|
|
if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0)
|
2000-11-29 00:27:57 +01:00
|
|
|
elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup");
|
2000-10-03 05:11:26 +02:00
|
|
|
#endif
|
1998-12-29 20:32:08 +01:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/*
|
|
|
|
* Now there is nothing for LockWaitCancel to do.
|
|
|
|
*/
|
|
|
|
waitingForLock = false;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ----------------
|
2001-01-14 06:08:17 +01:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
SpinAcquire(spinlock);
|
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/*
|
|
|
|
* We don't have to do anything else, because the awaker did all the
|
|
|
|
* necessary update of the lock table and MyProc.
|
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
return MyProc->errType;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcWakeup -- wake up a process by releasing its private semaphore.
|
|
|
|
*
|
2000-12-22 01:51:54 +01:00
|
|
|
* Also remove the process from the wait queue and set its links invalid.
|
1997-09-07 07:04:48 +02:00
|
|
|
* RETURN: the next process in the wait queue.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1999-05-13 17:55:45 +02:00
|
|
|
PROC *
|
1997-09-08 23:56:23 +02:00
|
|
|
ProcWakeup(PROC *proc, int errType)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
PROC *retProc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* assume that spinlock has been acquired */
|
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* Proc should be sleeping ... */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (proc->links.prev == INVALID_OFFSET ||
|
|
|
|
proc->links.next == INVALID_OFFSET)
|
1998-09-01 05:29:17 +02:00
|
|
|
return (PROC *) NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* Save next process before we zap the list link */
|
|
|
|
retProc = (PROC *) MAKE_PTR(proc->links.next);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* Remove process from wait queue */
|
1997-09-07 07:04:48 +02:00
|
|
|
SHMQueueDelete(&(proc->links));
|
2000-12-22 01:51:54 +01:00
|
|
|
(proc->waitLock->waitProcs.size)--;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* Clean up process' state and pass it the ok/fail signal */
|
|
|
|
proc->waitLock = NULL;
|
|
|
|
proc->waitHolder = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
proc->errType = errType;
|
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
/* And awaken it */
|
2000-11-29 00:27:57 +01:00
|
|
|
IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return retProc;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcLockWakeup -- routine for waking up processes when a lock is
|
1997-09-07 07:04:48 +02:00
|
|
|
* released.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
2000-12-22 01:51:54 +01:00
|
|
|
ProcLockWakeup(LOCKMETHOD lockmethod, LOCK *lock)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
PROC_QUEUE *queue = &(lock->waitProcs);
|
1997-09-08 04:41:22 +02:00
|
|
|
PROC *proc;
|
2000-12-22 01:51:54 +01:00
|
|
|
int awoken = 0;
|
|
|
|
LOCKMODE last_lockmode = 0;
|
1998-08-25 23:20:32 +02:00
|
|
|
int queue_size = queue->size;
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
Assert(queue_size >= 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (!queue_size)
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_NOT_FOUND;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-22 23:30:06 +01:00
|
|
|
proc = (PROC *) MAKE_PTR(queue->links.next);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
while (queue_size-- > 0)
|
|
|
|
{
|
|
|
|
if (proc->waitLockMode == last_lockmode)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This proc will conflict as the previous one did, don't even
|
|
|
|
* try.
|
|
|
|
*/
|
|
|
|
goto nextProc;
|
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
|
|
|
|
/*
|
1999-05-07 03:23:11 +02:00
|
|
|
* Does this proc conflict with locks held by others ?
|
1998-08-25 23:20:32 +02:00
|
|
|
*/
|
|
|
|
if (LockResolveConflicts(lockmethod,
|
2000-12-22 01:51:54 +01:00
|
|
|
proc->waitLockMode,
|
1998-06-30 04:33:34 +02:00
|
|
|
lock,
|
2000-12-22 01:51:54 +01:00
|
|
|
proc->waitHolder,
|
|
|
|
proc,
|
|
|
|
NULL) != STATUS_OK)
|
1998-08-25 23:20:32 +02:00
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
/* Yes. Quit if we already awoke at least one process. */
|
|
|
|
if (awoken != 0)
|
1999-05-07 03:23:11 +02:00
|
|
|
break;
|
2000-12-22 01:51:54 +01:00
|
|
|
/* Otherwise, see if any later waiters can be awoken. */
|
|
|
|
last_lockmode = proc->waitLockMode;
|
|
|
|
goto nextProc;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* OK to wake up this sleeping process.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
GrantLock(lock, proc->waitHolder, proc->waitLockMode);
|
|
|
|
proc = ProcWakeup(proc, NO_ERROR);
|
|
|
|
awoken++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-12-22 01:51:54 +01:00
|
|
|
* ProcWakeup removes proc from the lock's waiting process queue
|
2001-01-22 23:30:06 +01:00
|
|
|
* and returns the next proc in chain; don't use proc's next-link,
|
|
|
|
* because it's been cleared.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-12-22 01:51:54 +01:00
|
|
|
continue;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
nextProc:
|
2001-01-22 23:30:06 +01:00
|
|
|
proc = (PROC *) MAKE_PTR(proc->links.next);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:20:32 +02:00
|
|
|
Assert(queue->size >= 0);
|
|
|
|
|
2000-12-22 01:51:54 +01:00
|
|
|
if (awoken)
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_OK;
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
/* Something is still blocking us. May have deadlocked. */
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (lock->tag.lockmethod == USER_LOCKMETHOD ? Trace_userlocks : Trace_locks)
|
|
|
|
{
|
2000-12-22 01:51:54 +01:00
|
|
|
elog(DEBUG, "ProcLockWakeup: lock(%lx) can't wake up any process",
|
|
|
|
MAKE_OFFSET(lock));
|
2000-05-31 02:28:42 +02:00
|
|
|
if (Debug_deadlocks)
|
2000-12-22 01:51:54 +01:00
|
|
|
DumpAllLocks();
|
2000-05-31 02:28:42 +02:00
|
|
|
}
|
1998-08-25 23:20:32 +02:00
|
|
|
#endif
|
1998-09-01 05:29:17 +02:00
|
|
|
return STATUS_NOT_FOUND;
|
1998-08-25 23:20:32 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------
|
2000-05-31 02:28:42 +02:00
|
|
|
* We only get to this routine if we got SIGALRM after DeadlockTimeout
|
2001-01-14 06:08:17 +01:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
* --------------------
|
|
|
|
*/
|
1999-10-06 23:58:18 +02:00
|
|
|
void
|
2000-08-29 11:36:51 +02:00
|
|
|
HandleDeadLock(SIGNAL_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-12-18 18:33:42 +01:00
|
|
|
int save_errno = errno;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
LockLockTable();
|
|
|
|
|
|
|
|
/* ---------------------
|
|
|
|
* Check to see if we've been awoken by anyone in the interim.
|
|
|
|
*
|
|
|
|
* If we have we can return and resume our transaction -- happy day.
|
|
|
|
* Before we are awoken the process releasing the lock grants it to
|
|
|
|
* us so we know that we don't have to wait anymore.
|
|
|
|
*
|
2000-11-29 00:27:57 +01:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ---------------------
|
|
|
|
*/
|
|
|
|
if (MyProc->links.prev == INVALID_OFFSET ||
|
|
|
|
MyProc->links.next == INVALID_OFFSET)
|
|
|
|
{
|
|
|
|
UnlockLockTable();
|
2000-12-18 18:33:42 +01:00
|
|
|
errno = save_errno;
|
1997-09-07 07:04:48 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2000-05-31 02:28:42 +02:00
|
|
|
#ifdef LOCK_DEBUG
|
|
|
|
if (Debug_deadlocks)
|
|
|
|
DumpAllLocks();
|
1997-02-12 06:25:13 +01:00
|
|
|
#endif
|
|
|
|
|
1999-05-13 17:55:45 +02:00
|
|
|
if (!DeadLockCheck(MyProc, MyProc->waitLock))
|
1998-01-27 04:00:43 +01:00
|
|
|
{
|
2000-11-29 00:27:57 +01:00
|
|
|
/* No deadlock, so keep waiting */
|
1998-01-27 04:00:43 +01:00
|
|
|
UnlockLockTable();
|
2000-12-18 18:33:42 +01:00
|
|
|
errno = save_errno;
|
1998-01-27 04:00:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* ------------------------
|
2001-01-14 06:08:17 +01:00
|
|
|
* Oops. We have a deadlock.
|
|
|
|
*
|
|
|
|
* Get this process out of wait state.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ------------------------
|
|
|
|
*/
|
2001-01-14 06:08:17 +01:00
|
|
|
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;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* ------------------
|
2000-11-29 00:27:57 +01:00
|
|
|
* Unlock my semaphore so that the interrupted ProcSleep() call can finish.
|
1997-09-07 07:04:48 +02:00
|
|
|
* ------------------
|
|
|
|
*/
|
2000-11-29 00:27:57 +01:00
|
|
|
IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
/* ------------------
|
|
|
|
* 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.
|
|
|
|
* ------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
UnlockLockTable();
|
2000-12-18 18:33:42 +01:00
|
|
|
errno = save_errno;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
ProcReleaseSpins(PROC *proc)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!proc)
|
|
|
|
proc = MyProc;
|
|
|
|
|
|
|
|
if (!proc)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < (int) MAX_SPINS; i++)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (proc->sLocks[i])
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
Assert(proc->sLocks[i] == 1);
|
|
|
|
SpinRelease(i);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
2000-01-17 02:15:19 +01:00
|
|
|
AbortBufferIO();
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
2000-11-29 00:27:57 +01:00
|
|
|
* ProcGetNewSemIdAndNum -
|
1997-09-07 07:04:48 +02:00
|
|
|
* scan the free semaphore bitmap and allocate a single semaphore from
|
2000-11-29 00:27:57 +01:00
|
|
|
* a semaphore set.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
static void
|
2000-11-29 00:27:57 +01:00
|
|
|
ProcGetNewSemIdAndNum(IpcSemaphoreId *semId, int *semNum)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
2000-11-29 00:27:57 +01:00
|
|
|
IpcSemaphoreId *procSemIds = ProcGlobal->procSemIds;
|
1997-09-08 04:41:22 +02:00
|
|
|
int32 *freeSemMap = ProcGlobal->freeSemMap;
|
2000-11-29 00:27:57 +01:00
|
|
|
int32 fullmask = (1 << PROC_NSEMS_PER_SET) - 1;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* we hold ProcStructLock when entering this routine. We scan through
|
|
|
|
* the bitmap to look for a free semaphore.
|
|
|
|
*/
|
1999-02-19 07:06:39 +01:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int mask = 1;
|
|
|
|
int j;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (freeSemMap[i] == fullmask)
|
1999-02-19 07:06:39 +01:00
|
|
|
continue; /* this set is fully allocated */
|
2000-11-29 00:27:57 +01:00
|
|
|
if (procSemIds[i] < 0)
|
|
|
|
continue; /* this set hasn't been initialized */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
for (j = 0; j < PROC_NSEMS_PER_SET; j++)
|
|
|
|
{
|
|
|
|
if ((freeSemMap[i] & mask) == 0)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
2000-11-29 00:27:57 +01:00
|
|
|
* a free semaphore found. Mark it as allocated.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-11-29 00:27:57 +01:00
|
|
|
freeSemMap[i] |= mask;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
*semId = procSemIds[i];
|
1997-09-07 07:04:48 +02:00
|
|
|
*semNum = j;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mask <<= 1;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* if we reach here, all the semaphores are in use. */
|
2000-11-29 00:27:57 +01:00
|
|
|
elog(ERROR, "ProcGetNewSemIdAndNum: cannot allocate a free semaphore");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcFreeSem -
|
1999-02-19 07:06:39 +01:00
|
|
|
* free up our semaphore in the semaphore set.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
static void
|
2000-11-29 00:27:57 +01:00
|
|
|
ProcFreeSem(IpcSemaphoreId semId, int semNum)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-11-29 00:27:57 +01:00
|
|
|
int32 mask;
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
mask = ~(1 << semNum);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
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);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ProcFreeAllSemaphores -
|
1999-02-19 07:06:39 +01:00
|
|
|
* called at shmem_exit time, ie when exiting the postmaster or
|
|
|
|
* destroying shared state for a failed set of backends.
|
|
|
|
* Free up all the semaphores allocated to the lmgrs of the backends.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1999-02-19 08:10:48 +01:00
|
|
|
static void
|
2000-11-29 00:27:57 +01:00
|
|
|
ProcFreeAllSemaphores(void)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-11-29 00:27:57 +01:00
|
|
|
for (i = 0; i < PROC_SEM_MAP_ENTRIES; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-11-29 00:27:57 +01:00
|
|
|
if (ProcGlobal->procSemIds[i] >= 0)
|
|
|
|
IpcSemaphoreKill(ProcGlobal->procSemIds[i]);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|