mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
15
src/backend/storage/ipc/Makefile.inc
Normal file
15
src/backend/storage/ipc/Makefile.inc
Normal file
@@ -0,0 +1,15 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for storage/ipc
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS+= ipc.c ipci.c s_lock.c shmem.c shmqueue.c sinval.c \
|
||||
sinvaladt.c spin.c
|
||||
31
src/backend/storage/ipc/README
Normal file
31
src/backend/storage/ipc/README
Normal file
@@ -0,0 +1,31 @@
|
||||
$Header: /cvsroot/pgsql/src/backend/storage/ipc/README,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
Mon Jul 18 11:09:22 PDT 1988 W.KLAS
|
||||
|
||||
Cache invalidation synchronization routines:
|
||||
===========================================
|
||||
|
||||
The cache synchronization is done using a message queue. Every
|
||||
backend can register a message which then has to be read by
|
||||
all backends. A message read by all backends is removed from the
|
||||
queue automatically. If a message has been lost because the buffer
|
||||
was full, all backends that haven't read this message will be
|
||||
noticed that they have to reset their cache state. This is done
|
||||
at the time when they try to read the message queue.
|
||||
|
||||
The message queue is implemented as a shared buffer segment. Actually,
|
||||
the queue is a circle to allow fast inserting, reading (invalidate data) and
|
||||
maintaining the buffer.
|
||||
|
||||
Access to this shared message buffer is synchronized by the lock manager.
|
||||
The lock manager treats the buffer as a regular relation and sets
|
||||
relation level locks (with mode = LockWait) to block backends while
|
||||
another backend is writing or reading the buffer. The identifiers used
|
||||
for this special 'relation' are database id = 0 and relation id = 0.
|
||||
|
||||
The current implementation prints regular (e)log information
|
||||
when a message has been removed from the buffer because the buffer
|
||||
is full, and a backend has to reset its cache state. The elog level
|
||||
is NOTICE. This can be used to improve teh behavior of backends
|
||||
when invalidating or reseting their cache state.
|
||||
|
||||
|
||||
718
src/backend/storage/ipc/ipc.c
Normal file
718
src/backend/storage/ipc/ipc.c
Normal file
@@ -0,0 +1,718 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ipc.c--
|
||||
* POSTGRES inter-process communication definitions.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* Currently, semaphores are used (my understanding anyway) in two
|
||||
* different ways:
|
||||
* 1. as mutexes on machines that don't have test-and-set (eg.
|
||||
* mips R3000).
|
||||
* 2. for putting processes to sleep when waiting on a lock
|
||||
* and waking them up when the lock is free.
|
||||
* The number of semaphores in (1) is fixed and those are shared
|
||||
* among all backends. In (2), there is 1 semaphore per process and those
|
||||
* are not shared with anyone else.
|
||||
* -ay 4/95
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* XXX - the following dependency should be moved into the defaults.mk file */
|
||||
#ifndef _IPC_
|
||||
#define _IPC_
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/sem.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
#include "storage/ipc.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
#if defined(PORTNAME_bsd44)
|
||||
int UsePrivateMemory = 1;
|
||||
#else
|
||||
int UsePrivateMemory = 0;
|
||||
#endif
|
||||
|
||||
#if defined(PORTNAME_bsdi)
|
||||
/* hacka, hacka, hacka (XXX) */
|
||||
union semun {
|
||||
int val; /* value for SETVAL */
|
||||
struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
|
||||
ushort *array; /* array for GETALL & SETALL */
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* exit() handling stuff
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define MAX_ON_EXITS 20
|
||||
|
||||
static struct ONEXIT {
|
||||
void (*function)();
|
||||
caddr_t arg;
|
||||
} onexit_list[ MAX_ON_EXITS ];
|
||||
|
||||
static int onexit_index;
|
||||
|
||||
typedef struct _PrivateMemStruct {
|
||||
int id;
|
||||
char *memptr;
|
||||
} PrivateMem;
|
||||
|
||||
PrivateMem IpcPrivateMem[16];
|
||||
|
||||
static int
|
||||
PrivateMemoryCreate(IpcMemoryKey memKey,
|
||||
uint32 size)
|
||||
{
|
||||
static int memid = 0;
|
||||
|
||||
UsePrivateMemory = 1;
|
||||
|
||||
IpcPrivateMem[memid].id = memid;
|
||||
IpcPrivateMem[memid].memptr = malloc(size);
|
||||
if (IpcPrivateMem[memid].memptr == NULL)
|
||||
elog(WARN, "PrivateMemoryCreate: not enough memory to malloc");
|
||||
memset(IpcPrivateMem[memid].memptr, 0, size); /* XXX PURIFY */
|
||||
|
||||
return (memid++);
|
||||
}
|
||||
|
||||
static char *
|
||||
PrivateMemoryAttach(IpcMemoryId memid)
|
||||
{
|
||||
return ( IpcPrivateMem[memid].memptr );
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* exitpg
|
||||
*
|
||||
* this function calls all the callbacks registered
|
||||
* for it (to free resources) and then calls exit.
|
||||
* This should be the only function to call exit().
|
||||
* -cim 2/6/90
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static int exitpg_inprogress = 0;
|
||||
|
||||
void
|
||||
exitpg(int code)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* ----------------
|
||||
* if exitpg_inprocess is true, then it means that we
|
||||
* are being invoked from within an on_exit() handler
|
||||
* and so we return immediately to avoid recursion.
|
||||
* ----------------
|
||||
*/
|
||||
if (exitpg_inprogress)
|
||||
return;
|
||||
|
||||
exitpg_inprogress = 1;
|
||||
|
||||
/* ----------------
|
||||
* call all the callbacks registered before calling exit().
|
||||
* ----------------
|
||||
*/
|
||||
for (i = onexit_index - 1; i >= 0; --i)
|
||||
(*onexit_list[i].function)(code, onexit_list[i].arg);
|
||||
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* ------------------
|
||||
* Run all of the on_exitpg routines but don't exit in the end.
|
||||
* This is used by the postmaster to re-initialize shared memory and
|
||||
* semaphores after a backend dies horribly
|
||||
* ------------------
|
||||
*/
|
||||
void
|
||||
quasi_exitpg()
|
||||
{
|
||||
int i;
|
||||
|
||||
/* ----------------
|
||||
* if exitpg_inprocess is true, then it means that we
|
||||
* are being invoked from within an on_exit() handler
|
||||
* and so we return immediately to avoid recursion.
|
||||
* ----------------
|
||||
*/
|
||||
if (exitpg_inprogress)
|
||||
return;
|
||||
|
||||
exitpg_inprogress = 1;
|
||||
|
||||
/* ----------------
|
||||
* call all the callbacks registered before calling exit().
|
||||
* ----------------
|
||||
*/
|
||||
for (i = onexit_index - 1; i >= 0; --i)
|
||||
(*onexit_list[i].function)(0, onexit_list[i].arg);
|
||||
|
||||
onexit_index = 0;
|
||||
exitpg_inprogress = 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* on_exitpg
|
||||
*
|
||||
* this function adds a callback function to the list of
|
||||
* functions invoked by exitpg(). -cim 2/6/90
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
on_exitpg(void (*function)(), caddr_t arg)
|
||||
{
|
||||
if (onexit_index >= MAX_ON_EXITS)
|
||||
return(-1);
|
||||
|
||||
onexit_list[ onexit_index ].function = function;
|
||||
onexit_list[ onexit_index ].arg = arg;
|
||||
|
||||
++onexit_index;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IPCPrivateSemaphoreKill(status, semId) */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
static void
|
||||
IPCPrivateSemaphoreKill(int status,
|
||||
int semId) /* caddr_t */
|
||||
{
|
||||
union semun semun;
|
||||
semctl(semId, 0, IPC_RMID, semun);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* IPCPrivateMemoryKill(status, shmId) */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
static void
|
||||
IPCPrivateMemoryKill(int status,
|
||||
int shmId) /* caddr_t */
|
||||
{
|
||||
if ( UsePrivateMemory ) {
|
||||
/* free ( IpcPrivateMem[shmId].memptr ); */
|
||||
} else {
|
||||
if (shmctl(shmId, IPC_RMID, (struct shmid_ds *) NULL) < 0) {
|
||||
elog(NOTICE, "IPCPrivateMemoryKill: shmctl(%d, %d, 0) failed: %m",
|
||||
shmId, IPC_RMID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcSemaphoreCreate(semKey, semNum, permission, semStartValue) */
|
||||
/* */
|
||||
/* - returns a semaphore identifier: */
|
||||
/* */
|
||||
/* if key doesn't exist: return a new id, status:= IpcSemIdNotExist */
|
||||
/* if key exists: return the old id, status:= IpcSemIdExist */
|
||||
/* if semNum > MAX : return # of argument, status:=IpcInvalidArgument */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* XXX This should be split into two different calls. One should
|
||||
* XXX be used to create a semaphore set. The other to "attach" a
|
||||
* XXX existing set. It should be an error for the semaphore set
|
||||
* XXX to to already exist or for it not to, respectively.
|
||||
*
|
||||
* Currently, the semaphore sets are "attached" and an error
|
||||
* is detected only when a later shared memory attach fails.
|
||||
*/
|
||||
|
||||
IpcSemaphoreId
|
||||
IpcSemaphoreCreate(IpcSemaphoreKey semKey,
|
||||
int semNum,
|
||||
int permission,
|
||||
int semStartValue,
|
||||
int removeOnExit,
|
||||
int *status)
|
||||
{
|
||||
int i;
|
||||
int errStatus;
|
||||
int semId;
|
||||
u_short array[IPC_NMAXSEM];
|
||||
union semun semun;
|
||||
|
||||
/* get a semaphore if non-existent */
|
||||
/* check arguments */
|
||||
if (semNum > IPC_NMAXSEM || semNum <= 0) {
|
||||
*status = IpcInvalidArgument;
|
||||
return(2); /* returns the number of the invalid argument */
|
||||
}
|
||||
|
||||
semId = semget(semKey, 0, 0);
|
||||
|
||||
if (semId == -1) {
|
||||
*status = IpcSemIdNotExist; /* there doesn't exist a semaphore */
|
||||
#ifdef DEBUG_IPC
|
||||
fprintf(stderr,"calling semget with %d, %d , %d\n",
|
||||
semKey,
|
||||
semNum,
|
||||
IPC_CREAT|permission );
|
||||
#endif
|
||||
semId = semget(semKey, semNum, IPC_CREAT|permission);
|
||||
|
||||
if (semId < 0) {
|
||||
perror("semget");
|
||||
exitpg(3);
|
||||
}
|
||||
for (i = 0; i < semNum; i++) {
|
||||
array[i] = semStartValue;
|
||||
}
|
||||
semun.array = array;
|
||||
errStatus = semctl(semId, 0, SETALL, semun);
|
||||
if (errStatus == -1) {
|
||||
perror("semctl");
|
||||
}
|
||||
|
||||
if (removeOnExit)
|
||||
on_exitpg(IPCPrivateSemaphoreKill, (caddr_t)semId);
|
||||
|
||||
} else {
|
||||
/* there is a semaphore id for this key */
|
||||
*status = IpcSemIdExist;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_IPC
|
||||
fprintf(stderr,"\nIpcSemaphoreCreate, status %d, returns %d\n",
|
||||
*status,
|
||||
semId );
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
return(semId);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcSemaphoreSet() - sets the initial value of the semaphore */
|
||||
/* */
|
||||
/* note: the xxx_return variables are only used for debugging. */
|
||||
/****************************************************************************/
|
||||
static int IpcSemaphoreSet_return;
|
||||
|
||||
void
|
||||
IpcSemaphoreSet(int semId, int semno, int value)
|
||||
{
|
||||
int errStatus;
|
||||
union semun semun;
|
||||
|
||||
semun.val = value;
|
||||
errStatus = semctl(semId, semno, SETVAL, semun);
|
||||
IpcSemaphoreSet_return = errStatus;
|
||||
|
||||
if (errStatus == -1)
|
||||
perror("semctl");
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcSemaphoreKill(key) - removes a semaphore */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
void
|
||||
IpcSemaphoreKill(IpcSemaphoreKey key)
|
||||
{
|
||||
int semId;
|
||||
union semun semun;
|
||||
|
||||
/* kill semaphore if existent */
|
||||
|
||||
semId = semget(key, 0, 0);
|
||||
if (semId != -1)
|
||||
semctl(semId, 0, IPC_RMID, semun);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcSemaphoreLock(semId, sem, lock) - locks a semaphore */
|
||||
/* */
|
||||
/* note: the xxx_return variables are only used for debugging. */
|
||||
/****************************************************************************/
|
||||
static int IpcSemaphoreLock_return;
|
||||
|
||||
void
|
||||
IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
|
||||
{
|
||||
extern int errno;
|
||||
int errStatus;
|
||||
struct sembuf sops;
|
||||
|
||||
sops.sem_op = lock;
|
||||
sops.sem_flg = 0;
|
||||
sops.sem_num = 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.
|
||||
* I am not certain this is correct, but the semantics aren't
|
||||
* clear it fixes problems with parallel abort synchronization,
|
||||
* namely that after processing an abort signal, the semaphore
|
||||
* call returns with -1 (and errno == EINTR) before it should.
|
||||
* -cim 3/28/90
|
||||
* ----------------
|
||||
*/
|
||||
do {
|
||||
errStatus = semop(semId, &sops, 1);
|
||||
} while (errStatus == -1 && errno == EINTR);
|
||||
|
||||
IpcSemaphoreLock_return = errStatus;
|
||||
|
||||
if (errStatus == -1) {
|
||||
perror("semop");
|
||||
exitpg(255);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcSemaphoreUnlock(semId, sem, lock) - unlocks a semaphore */
|
||||
/* */
|
||||
/* note: the xxx_return variables are only used for debugging. */
|
||||
/****************************************************************************/
|
||||
static int IpcSemaphoreUnlock_return;
|
||||
|
||||
void
|
||||
IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
|
||||
{
|
||||
extern int errno;
|
||||
int errStatus;
|
||||
struct sembuf sops;
|
||||
|
||||
sops.sem_op = -lock;
|
||||
sops.sem_flg = 0;
|
||||
sops.sem_num = 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.
|
||||
* I am not certain this is correct, but the semantics aren't
|
||||
* clear it fixes problems with parallel abort synchronization,
|
||||
* namely that after processing an abort signal, the semaphore
|
||||
* call returns with -1 (and errno == EINTR) before it should.
|
||||
* -cim 3/28/90
|
||||
* ----------------
|
||||
*/
|
||||
do {
|
||||
errStatus = semop(semId, &sops, 1);
|
||||
} while (errStatus == -1 && errno == EINTR);
|
||||
|
||||
IpcSemaphoreUnlock_return = errStatus;
|
||||
|
||||
if (errStatus == -1) {
|
||||
perror("semop");
|
||||
exitpg(255);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem)
|
||||
{
|
||||
int semncnt;
|
||||
union semun dummy; /* for Solaris */
|
||||
|
||||
semncnt = semctl(semId, sem, GETNCNT, dummy);
|
||||
return semncnt;
|
||||
}
|
||||
|
||||
int
|
||||
IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem)
|
||||
{
|
||||
int semval;
|
||||
union semun dummy; /* for Solaris */
|
||||
|
||||
semval = semctl(semId, sem, GETVAL, dummy);
|
||||
return semval;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcMemoryCreate(memKey) */
|
||||
/* */
|
||||
/* - returns the memory identifier, if creation succeeds */
|
||||
/* returns IpcMemCreationFailed, if failure */
|
||||
/****************************************************************************/
|
||||
|
||||
IpcMemoryId
|
||||
IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
|
||||
{
|
||||
IpcMemoryId shmid;
|
||||
|
||||
if (memKey == PrivateIPCKey) {
|
||||
/* private */
|
||||
shmid = PrivateMemoryCreate(memKey, size);
|
||||
}else {
|
||||
shmid = shmget(memKey, size, IPC_CREAT|permission);
|
||||
}
|
||||
|
||||
if (shmid < 0) {
|
||||
fprintf(stderr,"IpcMemoryCreate: memKey=%d , size=%d , permission=%d",
|
||||
memKey, size , permission );
|
||||
perror("IpcMemoryCreate: shmget(..., create, ...) failed");
|
||||
return(IpcMemCreationFailed);
|
||||
}
|
||||
|
||||
/* if (memKey == PrivateIPCKey) */
|
||||
on_exitpg(IPCPrivateMemoryKill, (caddr_t)shmid);
|
||||
|
||||
return(shmid);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcMemoryIdGet(memKey, size) returns the shared memory Id */
|
||||
/* or IpcMemIdGetFailed */
|
||||
/****************************************************************************/
|
||||
IpcMemoryId
|
||||
IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size)
|
||||
{
|
||||
IpcMemoryId shmid;
|
||||
|
||||
shmid = shmget(memKey, size, 0);
|
||||
|
||||
if (shmid < 0) {
|
||||
fprintf(stderr,"IpcMemoryIdGet: memKey=%d , size=%d , permission=%d",
|
||||
memKey, size , 0 );
|
||||
perror("IpcMemoryIdGet: shmget() failed");
|
||||
return(IpcMemIdGetFailed);
|
||||
}
|
||||
|
||||
return(shmid);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
|
||||
/* from a backend address space */
|
||||
/* (only called by backends running under the postmaster) */
|
||||
/****************************************************************************/
|
||||
void
|
||||
IpcMemoryDetach(int status, char *shmaddr)
|
||||
{
|
||||
if (shmdt(shmaddr) < 0) {
|
||||
elog(NOTICE, "IpcMemoryDetach: shmdt(0x%x): %m", shmaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcMemoryAttach(memId) returns the adress of shared memory */
|
||||
/* or IpcMemAttachFailed */
|
||||
/* */
|
||||
/* CALL IT: addr = (struct <MemoryStructure> *) IpcMemoryAttach(memId); */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
char *
|
||||
IpcMemoryAttach(IpcMemoryId memId)
|
||||
{
|
||||
char *memAddress;
|
||||
|
||||
if (UsePrivateMemory) {
|
||||
memAddress = (char *) PrivateMemoryAttach(memId);
|
||||
} else {
|
||||
memAddress = (char *) shmat(memId, 0, 0);
|
||||
}
|
||||
|
||||
/* if ( *memAddress == -1) { XXX ??? */
|
||||
if ( memAddress == (char *)-1) {
|
||||
perror("IpcMemoryAttach: shmat() failed");
|
||||
return(IpcMemAttachFailed);
|
||||
}
|
||||
|
||||
if (!UsePrivateMemory)
|
||||
on_exitpg(IpcMemoryDetach, (caddr_t) memAddress);
|
||||
|
||||
return((char *) memAddress);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* IpcMemoryKill(memKey) removes a shared memory segment */
|
||||
/* (only called by the postmaster and standalone backends) */
|
||||
/****************************************************************************/
|
||||
void
|
||||
IpcMemoryKill(IpcMemoryKey memKey)
|
||||
{
|
||||
IpcMemoryId shmid;
|
||||
|
||||
if (!UsePrivateMemory && (shmid = shmget(memKey, 0, 0)) >= 0) {
|
||||
if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0) {
|
||||
elog(NOTICE, "IpcMemoryKill: shmctl(%d, %d, 0) failed: %m",
|
||||
shmid, IPC_RMID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_TEST_AND_SET
|
||||
/* ------------------
|
||||
* use hardware locks to replace semaphores for sequent machines
|
||||
* to avoid costs of swapping processes and to provide unlimited
|
||||
* supply of locks.
|
||||
* ------------------
|
||||
*/
|
||||
static SLock *SLockArray = NULL;
|
||||
static SLock **FreeSLockPP;
|
||||
static int *UnusedSLockIP;
|
||||
static slock_t *SLockMemoryLock;
|
||||
static IpcMemoryId SLockMemoryId = -1;
|
||||
|
||||
struct ipcdummy { /* to get alignment/size right */
|
||||
SLock *free;
|
||||
int unused;
|
||||
slock_t memlock;
|
||||
SLock slocks[NSLOCKS];
|
||||
};
|
||||
static int SLockMemorySize = sizeof(struct ipcdummy);
|
||||
|
||||
void
|
||||
CreateAndInitSLockMemory(IPCKey key)
|
||||
{
|
||||
int id;
|
||||
SLock *slckP;
|
||||
|
||||
SLockMemoryId = IpcMemoryCreate(key,
|
||||
SLockMemorySize,
|
||||
0700);
|
||||
AttachSLockMemory(key);
|
||||
*FreeSLockPP = NULL;
|
||||
*UnusedSLockIP = (int)FIRSTFREELOCKID;
|
||||
for (id=0; id<(int)FIRSTFREELOCKID; id++) {
|
||||
slckP = &(SLockArray[id]);
|
||||
S_INIT_LOCK(&(slckP->locklock));
|
||||
slckP->flag = NOLOCK;
|
||||
slckP->nshlocks = 0;
|
||||
S_INIT_LOCK(&(slckP->shlock));
|
||||
S_INIT_LOCK(&(slckP->exlock));
|
||||
S_INIT_LOCK(&(slckP->comlock));
|
||||
slckP->next = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
AttachSLockMemory(IPCKey key)
|
||||
{
|
||||
struct ipcdummy *slockM;
|
||||
|
||||
if (SLockMemoryId == -1)
|
||||
SLockMemoryId = IpcMemoryIdGet(key,SLockMemorySize);
|
||||
if (SLockMemoryId == -1)
|
||||
elog(FATAL, "SLockMemory not in shared memory");
|
||||
slockM = (struct ipcdummy *) IpcMemoryAttach(SLockMemoryId);
|
||||
if (slockM == IpcMemAttachFailed)
|
||||
elog(FATAL, "AttachSLockMemory: could not attach segment");
|
||||
FreeSLockPP = (SLock **) &(slockM->free);
|
||||
UnusedSLockIP = (int *) &(slockM->unused);
|
||||
SLockMemoryLock = (slock_t *) &(slockM->memlock);
|
||||
S_INIT_LOCK(SLockMemoryLock);
|
||||
SLockArray = (SLock *) &(slockM->slocks[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef LOCKDEBUG
|
||||
#define PRINT_LOCK(LOCK) printf("(locklock = %d, flag = %d, nshlocks = %d, \
|
||||
shlock = %d, exlock =%d)\n", LOCK->locklock, \
|
||||
LOCK->flag, LOCK->nshlocks, LOCK->shlock, \
|
||||
LOCK->exlock)
|
||||
#endif
|
||||
|
||||
void
|
||||
ExclusiveLock(int lockid)
|
||||
{
|
||||
SLock *slckP;
|
||||
slckP = &(SLockArray[lockid]);
|
||||
#ifdef LOCKDEBUG
|
||||
printf("ExclusiveLock(%d)\n", lockid);
|
||||
printf("IN: ");
|
||||
PRINT_LOCK(slckP);
|
||||
#endif
|
||||
ex_try_again:
|
||||
S_LOCK(&(slckP->locklock));
|
||||
switch (slckP->flag) {
|
||||
case NOLOCK:
|
||||
slckP->flag = EXCLUSIVELOCK;
|
||||
S_LOCK(&(slckP->exlock));
|
||||
S_LOCK(&(slckP->shlock));
|
||||
S_UNLOCK(&(slckP->locklock));
|
||||
#ifdef LOCKDEBUG
|
||||
printf("OUT: ");
|
||||
PRINT_LOCK(slckP);
|
||||
#endif
|
||||
return;
|
||||
case SHAREDLOCK:
|
||||
case EXCLUSIVELOCK:
|
||||
S_UNLOCK(&(slckP->locklock));
|
||||
S_LOCK(&(slckP->exlock));
|
||||
S_UNLOCK(&(slckP->exlock));
|
||||
goto ex_try_again;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExclusiveUnlock(int lockid)
|
||||
{
|
||||
SLock *slckP;
|
||||
|
||||
slckP = &(SLockArray[lockid]);
|
||||
#ifdef LOCKDEBUG
|
||||
printf("ExclusiveUnlock(%d)\n", lockid);
|
||||
printf("IN: ");
|
||||
PRINT_LOCK(slckP);
|
||||
#endif
|
||||
S_LOCK(&(slckP->locklock));
|
||||
/* -------------
|
||||
* give favor to read processes
|
||||
* -------------
|
||||
*/
|
||||
slckP->flag = NOLOCK;
|
||||
if (slckP->nshlocks > 0) {
|
||||
while (slckP->nshlocks > 0) {
|
||||
S_UNLOCK(&(slckP->shlock));
|
||||
S_LOCK(&(slckP->comlock));
|
||||
}
|
||||
S_UNLOCK(&(slckP->shlock));
|
||||
}
|
||||
else {
|
||||
S_UNLOCK(&(slckP->shlock));
|
||||
}
|
||||
S_UNLOCK(&(slckP->exlock));
|
||||
S_UNLOCK(&(slckP->locklock));
|
||||
#ifdef LOCKDEBUG
|
||||
printf("OUT: ");
|
||||
PRINT_LOCK(slckP);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
LockIsFree(int lockid)
|
||||
{
|
||||
return(SLockArray[lockid].flag == NOLOCK);
|
||||
}
|
||||
|
||||
#endif /* HAS_TEST_AND_SET */
|
||||
149
src/backend/storage/ipc/ipci.c
Normal file
149
src/backend/storage/ipc/ipci.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ipci.c--
|
||||
* POSTGRES inter-process communication initialization code.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "c.h"
|
||||
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/multilev.h"
|
||||
#include "utils/elog.h"
|
||||
#include "storage/sinval.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "storage/lock.h"
|
||||
#include "miscadmin.h" /* for DebugLvl */
|
||||
|
||||
/*
|
||||
* SystemPortAddressCreateMemoryKey --
|
||||
* Returns a memory key given a port address.
|
||||
*/
|
||||
IPCKey
|
||||
SystemPortAddressCreateIPCKey(SystemPortAddress address)
|
||||
{
|
||||
Assert(address < 32768); /* XXX */
|
||||
|
||||
return (SystemPortAddressGetIPCKey(address));
|
||||
}
|
||||
|
||||
/*
|
||||
* CreateSharedMemoryAndSemaphores --
|
||||
* Creates and initializes shared memory and semaphores.
|
||||
*/
|
||||
/**************************************************
|
||||
|
||||
CreateSharedMemoryAndSemaphores
|
||||
is called exactly *ONCE* by the postmaster.
|
||||
It is *NEVER* called by the postgres backend
|
||||
|
||||
0) destroy any existing semaphores for both buffer
|
||||
and lock managers.
|
||||
1) create the appropriate *SHARED* memory segments
|
||||
for the two resource managers.
|
||||
|
||||
**************************************************/
|
||||
|
||||
void
|
||||
CreateSharedMemoryAndSemaphores(IPCKey key)
|
||||
{
|
||||
int size;
|
||||
|
||||
#ifdef HAS_TEST_AND_SET
|
||||
/* ---------------
|
||||
* create shared memory for slocks
|
||||
* --------------
|
||||
*/
|
||||
CreateAndInitSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
|
||||
#endif
|
||||
/* ----------------
|
||||
* kill and create the buffer manager buffer pool (and semaphore)
|
||||
* ----------------
|
||||
*/
|
||||
CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key));
|
||||
size = BufferShmemSize() + LockShmemSize();
|
||||
|
||||
#ifdef MAIN_MEMORY
|
||||
size += MMShmemSize();
|
||||
#endif /* MAIN_MEMORY */
|
||||
|
||||
if (DebugLvl > 1) {
|
||||
fprintf(stderr, "binding ShmemCreate(key=%x, size=%d)\n",
|
||||
IPCKeyGetBufferMemoryKey(key), size);
|
||||
}
|
||||
ShmemCreate(IPCKeyGetBufferMemoryKey(key), size);
|
||||
ShmemBindingTabReset();
|
||||
InitShmem(key, size);
|
||||
InitBufferPool(key);
|
||||
|
||||
/* ----------------
|
||||
* do the lock table stuff
|
||||
* ----------------
|
||||
*/
|
||||
InitLocks();
|
||||
InitMultiLevelLockm();
|
||||
if (InitMultiLevelLockm() == INVALID_TABLEID)
|
||||
elog(FATAL, "Couldn't create the lock table");
|
||||
|
||||
/* ----------------
|
||||
* do process table stuff
|
||||
* ----------------
|
||||
*/
|
||||
InitProcGlobal(key);
|
||||
on_exitpg(ProcFreeAllSemaphores, 0);
|
||||
|
||||
CreateSharedInvalidationState(key);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AttachSharedMemoryAndSemaphores --
|
||||
* Attachs existant shared memory and semaphores.
|
||||
*/
|
||||
void
|
||||
AttachSharedMemoryAndSemaphores(IPCKey key)
|
||||
{
|
||||
int size;
|
||||
|
||||
/* ----------------
|
||||
* create rather than attach if using private key
|
||||
* ----------------
|
||||
*/
|
||||
if (key == PrivateIPCKey) {
|
||||
CreateSharedMemoryAndSemaphores(key);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAS_TEST_AND_SET
|
||||
/* ----------------
|
||||
* attach the slock shared memory
|
||||
* ----------------
|
||||
*/
|
||||
AttachSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
|
||||
#endif
|
||||
/* ----------------
|
||||
* attach the buffer manager buffer pool (and semaphore)
|
||||
* ----------------
|
||||
*/
|
||||
size = BufferShmemSize() + LockShmemSize();
|
||||
InitShmem(key, size);
|
||||
InitBufferPool(key);
|
||||
|
||||
/* ----------------
|
||||
* initialize lock table stuff
|
||||
* ----------------
|
||||
*/
|
||||
InitLocks();
|
||||
if (InitMultiLevelLockm() == INVALID_TABLEID)
|
||||
elog(FATAL, "Couldn't attach to the lock table");
|
||||
|
||||
AttachSharedInvalidationState(key);
|
||||
}
|
||||
440
src/backend/storage/ipc/s_lock.c
Normal file
440
src/backend/storage/ipc/s_lock.c
Normal file
@@ -0,0 +1,440 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* s_lock.c--
|
||||
* This file contains the implementation (if any) for spinlocks.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/s_lock.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* DESCRIPTION
|
||||
* The following code fragment should be written (in assembly
|
||||
* language) on machines that have a native test-and-set instruction:
|
||||
*
|
||||
* void
|
||||
* S_LOCK(char_address)
|
||||
* char *char_address;
|
||||
* {
|
||||
* while (test_and_set(char_address))
|
||||
* ;
|
||||
* }
|
||||
*
|
||||
* If this is not done, POSTGRES will default to using System V
|
||||
* semaphores (and take a large performance hit -- around 40% of
|
||||
* its time on a DS5000/240 is spent in semop(3)...).
|
||||
*
|
||||
* NOTES
|
||||
* AIX has a test-and-set but the recommended interface is the cs(3)
|
||||
* system call. This provides an 8-instruction (plus system call
|
||||
* overhead) uninterruptible compare-and-set operation. True
|
||||
* spinlocks might be faster but using cs(3) still speeds up the
|
||||
* regression test suite by about 25%. I don't have an assembler
|
||||
* manual for POWER in any case.
|
||||
*
|
||||
*/
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif /* WIN32 */
|
||||
#include "storage/ipc.h"
|
||||
|
||||
|
||||
#if defined(HAS_TEST_AND_SET)
|
||||
|
||||
#if defined (PORTNAME_next)
|
||||
/*
|
||||
* NEXTSTEP (mach)
|
||||
* slock_t is defined as a struct mutex.
|
||||
*/
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
mutex_lock(lock);
|
||||
}
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
mutex_unlock(lock);
|
||||
}
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
mutex_init(lock);
|
||||
}
|
||||
|
||||
/* S_LOCK_FREE should return 1 if lock is free; 0 if lock is locked */
|
||||
int
|
||||
S_LOCK_FREE(slock_t *lock)
|
||||
{
|
||||
/* For Mach, we have to delve inside the entrails of `struct
|
||||
mutex'. Ick! */
|
||||
return (lock->lock == 0);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_next */
|
||||
|
||||
|
||||
|
||||
#if defined(PORTNAME_irix5)
|
||||
/*
|
||||
* SGI IRIX 5
|
||||
* slock_t is defined as a struct abilock_t, which has a single unsigned long
|
||||
* member.
|
||||
*
|
||||
* This stuff may be supplemented in the future with Masato Kataoka's MIPS-II
|
||||
* assembly from his NECEWS SVR4 port, but we probably ought to retain this
|
||||
* for the R3000 chips out there.
|
||||
*/
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
/* spin_lock(lock); */
|
||||
while (!acquire_lock(lock))
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
(void)release_lock(lock);
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
(void)init_lock(lock);
|
||||
}
|
||||
|
||||
/* S_LOCK_FREE should return 1 if lock is free; 0 if lock is locked */
|
||||
int
|
||||
S_LOCK_FREE(slock_t *lock)
|
||||
{
|
||||
return(stat_lock(lock)==UNLOCKED);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_irix5 */
|
||||
|
||||
|
||||
/*
|
||||
* OSF/1 (Alpha AXP)
|
||||
*
|
||||
* Note that slock_t on the Alpha AXP is msemaphore instead of char
|
||||
* (see storage/ipc.h).
|
||||
*/
|
||||
|
||||
#if defined(PORTNAME_alpha)
|
||||
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
while (msem_lock(lock, MSEM_IF_NOWAIT) < 0)
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
(void) msem_unlock(lock, 0);
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
(void) msem_init(lock, MSEM_UNLOCKED);
|
||||
}
|
||||
|
||||
int
|
||||
S_LOCK_FREE(slock_t *lock)
|
||||
{
|
||||
return(lock->msem_state ? 0 : 1);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_alpha */
|
||||
|
||||
/*
|
||||
* Solaris 2
|
||||
*/
|
||||
|
||||
#if defined(PORTNAME_sparc_solaris)
|
||||
|
||||
/* defined in port/.../tas.s */
|
||||
extern int tas(slock_t *lock);
|
||||
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
while (tas(lock))
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
S_UNLOCK(lock);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_sparc_solaris */
|
||||
|
||||
/*
|
||||
* AIX (POWER)
|
||||
*
|
||||
* Note that slock_t on POWER/POWER2/PowerPC is int instead of char
|
||||
* (see storage/ipc.h).
|
||||
*/
|
||||
|
||||
#if defined(PORTNAME_aix)
|
||||
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
while (cs((int *) lock, 0, 1))
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
S_UNLOCK(lock);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_aix */
|
||||
|
||||
/*
|
||||
* HP-UX (PA-RISC)
|
||||
*
|
||||
* Note that slock_t on PA-RISC is a structure instead of char
|
||||
* (see storage/ipc.h).
|
||||
*/
|
||||
|
||||
#if defined(PORTNAME_hpux)
|
||||
|
||||
/* defined in port/.../tas.s */
|
||||
extern int tas(slock_t *lock);
|
||||
|
||||
/*
|
||||
* a "set" slock_t has a single word cleared. a "clear" slock_t has
|
||||
* all words set to non-zero.
|
||||
*/
|
||||
static slock_t clear_lock = { -1, -1, -1, -1 };
|
||||
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
while (tas(lock))
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
*lock = clear_lock; /* struct assignment */
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
S_UNLOCK(lock);
|
||||
}
|
||||
|
||||
int
|
||||
S_LOCK_FREE(slock_t *lock)
|
||||
{
|
||||
register int *lock_word = (int *) (((long) lock + 15) & ~15);
|
||||
|
||||
return(*lock_word != 0);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_hpux */
|
||||
|
||||
/*
|
||||
* sun3
|
||||
*/
|
||||
|
||||
#if (defined(sun) && ! defined(sparc))
|
||||
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
while (tas(lock));
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
S_UNLOCK(lock);
|
||||
}
|
||||
|
||||
static int
|
||||
tas_dummy()
|
||||
{
|
||||
asm("LLA0:");
|
||||
asm(" .data");
|
||||
asm(" .text");
|
||||
asm("|#PROC# 04");
|
||||
asm(" .globl _tas");
|
||||
asm("_tas:");
|
||||
asm("|#PROLOGUE# 1");
|
||||
asm(" movel sp@(0x4),a0");
|
||||
asm(" tas a0@");
|
||||
asm(" beq LLA1");
|
||||
asm(" moveq #-128,d0");
|
||||
asm(" rts");
|
||||
asm("LLA1:");
|
||||
asm(" moveq #0,d0");
|
||||
asm(" rts");
|
||||
asm(" .data");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SPARC (SunOS 4)
|
||||
*/
|
||||
|
||||
#if defined(PORTNAME_sparc)
|
||||
|
||||
/* if we're using -ansi w/ gcc, use __asm__ instead of asm */
|
||||
#if defined(__STRICT_ANSI__)
|
||||
#define asm(x) __asm__(x)
|
||||
#endif
|
||||
|
||||
static int
|
||||
tas_dummy()
|
||||
{
|
||||
asm(".seg \"data\"");
|
||||
asm(".seg \"text\"");
|
||||
asm(".global _tas");
|
||||
asm("_tas:");
|
||||
|
||||
/*
|
||||
* Sparc atomic test and set (sparc calls it "atomic load-store")
|
||||
*/
|
||||
|
||||
asm("ldstub [%r8], %r8");
|
||||
|
||||
/*
|
||||
* Did test and set actually do the set?
|
||||
*/
|
||||
|
||||
asm("tst %r8");
|
||||
|
||||
asm("be,a ReturnZero");
|
||||
|
||||
/*
|
||||
* otherwise, just return.
|
||||
*/
|
||||
|
||||
asm("clr %r8");
|
||||
asm("mov 0x1, %r8");
|
||||
asm("ReturnZero:");
|
||||
asm("retl");
|
||||
asm("nop");
|
||||
}
|
||||
|
||||
void
|
||||
S_LOCK(unsigned char *addr)
|
||||
{
|
||||
while (tas(addr));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* addr should be as in the above S_LOCK routine
|
||||
*/
|
||||
void
|
||||
S_UNLOCK(unsigned char *addr)
|
||||
{
|
||||
*addr = 0;
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(unsigned char *addr)
|
||||
{
|
||||
*addr = 0;
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_sparc */
|
||||
|
||||
/*
|
||||
* Linux and friends
|
||||
*/
|
||||
|
||||
#if defined(PORTNAME_linux) || defined(PORTNAME_BSD44_derived)
|
||||
|
||||
int
|
||||
tas(slock_t *m)
|
||||
{
|
||||
slock_t res;
|
||||
__asm__("xchgb %0,%1":"=q" (res),"=m" (*m):"0" (0x1));
|
||||
return(res);
|
||||
}
|
||||
|
||||
void
|
||||
S_LOCK(slock_t *lock)
|
||||
{
|
||||
while (tas(lock))
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(slock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(slock_t *lock)
|
||||
{
|
||||
S_UNLOCK(lock);
|
||||
}
|
||||
|
||||
#endif /* PORTNAME_linux || PORTNAME_BSD44_derived */
|
||||
|
||||
|
||||
#endif /* HAS_TEST_AND_SET */
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
void
|
||||
S_LOCK(HANDLE *lock)
|
||||
{
|
||||
int x = 0;
|
||||
x = x / x;
|
||||
}
|
||||
|
||||
void
|
||||
S_UNLOCK(HANDLE *lock)
|
||||
{
|
||||
int x = 0;
|
||||
x = x / x;
|
||||
}
|
||||
|
||||
void
|
||||
S_INIT_LOCK(HANDLE *lock)
|
||||
{
|
||||
int x = 0;
|
||||
x = x / x;
|
||||
}
|
||||
#endif /*WIN32*/
|
||||
561
src/backend/storage/ipc/shmem.c
Normal file
561
src/backend/storage/ipc/shmem.c
Normal file
@@ -0,0 +1,561 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* shmem.c--
|
||||
* create shared memory and initialize shared memory data structures.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* POSTGRES processes share one or more regions of shared memory.
|
||||
* The shared memory is created by a postmaster and is "attached to"
|
||||
* by each of the backends. The routines in this file are used for
|
||||
* allocating and binding to shared memory data structures.
|
||||
*
|
||||
* NOTES:
|
||||
* (a) There are three kinds of shared memory data structures
|
||||
* available to POSTGRES: fixed-size structures, queues and hash
|
||||
* tables. Fixed-size structures contain things like global variables
|
||||
* for a module and should never be allocated after the process
|
||||
* initialization phase. Hash tables have a fixed maximum size, but
|
||||
* their actual size can vary dynamically. When entries are added
|
||||
* to the table, more space is allocated. Queues link data structures
|
||||
* that have been allocated either as fixed size structures or as hash
|
||||
* buckets. Each shared data structure has a string name to identify
|
||||
* it (assigned in the module that declares it).
|
||||
*
|
||||
* (b) During initialization, each module looks for its
|
||||
* shared data structures in a hash table called the "Binding Table".
|
||||
* If the data structure is not present, the caller can allocate
|
||||
* a new one and initialize it. If the data structure is present,
|
||||
* the caller "attaches" to the structure by initializing a pointer
|
||||
* in the local address space.
|
||||
* The binding table has two purposes: first, it gives us
|
||||
* a simple model of how the world looks when a backend process
|
||||
* initializes. If something is present in the binding table,
|
||||
* it is initialized. If it is not, it is uninitialized. Second,
|
||||
* the binding table allows us to allocate shared memory on demand
|
||||
* instead of trying to preallocate structures and hard-wire the
|
||||
* sizes and locations in header files. If you are using a lot
|
||||
* of shared memory in a lot of different places (and changing
|
||||
* things during development), this is important.
|
||||
*
|
||||
* (c) memory allocation model: shared memory can never be
|
||||
* freed, once allocated. Each hash table has its own free list,
|
||||
* so hash buckets can be reused when an item is deleted. However,
|
||||
* if one hash table grows very large and then shrinks, its space
|
||||
* cannot be redistributed to other tables. We could build a simple
|
||||
* hash bucket garbage collector if need be. Right now, it seems
|
||||
* unnecessary.
|
||||
*
|
||||
* See InitSem() in sem.c for an example of how to use the
|
||||
* binding table.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "postgres.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/spin.h"
|
||||
#include "utils/hsearch.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
/* shared memory global variables */
|
||||
|
||||
unsigned long ShmemBase = 0; /* start and end address of
|
||||
* shared memory
|
||||
*/
|
||||
static unsigned long ShmemEnd = 0;
|
||||
static unsigned long ShmemSize = 0; /* current size (and default) */
|
||||
|
||||
SPINLOCK ShmemLock; /* lock for shared memory allocation */
|
||||
|
||||
SPINLOCK BindingLock; /* lock for binding table access */
|
||||
|
||||
static unsigned long *ShmemFreeStart = NULL; /* pointer to the OFFSET of
|
||||
* first free shared memory
|
||||
*/
|
||||
static unsigned long *ShmemBindingTabOffset = NULL; /* start of the binding
|
||||
* table (for bootstrap)
|
||||
*/
|
||||
static int ShmemBootstrap = FALSE; /* flag becomes true when shared mem
|
||||
* is created by POSTMASTER
|
||||
*/
|
||||
|
||||
static HTAB *BindingTable = NULL;
|
||||
|
||||
/* ---------------------
|
||||
* ShmemBindingTabReset() - Resets the binding table to NULL....
|
||||
* useful when the postmaster destroys existing shared memory
|
||||
* and creates all new segments after a backend crash.
|
||||
* ----------------------
|
||||
*/
|
||||
void
|
||||
ShmemBindingTabReset()
|
||||
{
|
||||
BindingTable = (HTAB *)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* CreateSharedRegion() --
|
||||
*
|
||||
* This routine is called once by the postmaster to
|
||||
* initialize the shared buffer pool. Assume there is
|
||||
* only one postmaster so no synchronization is necessary
|
||||
* until after this routine completes successfully.
|
||||
*
|
||||
* key is a unique identifier for the shmem region.
|
||||
* size is the size of the region.
|
||||
*/
|
||||
static IpcMemoryId ShmemId;
|
||||
|
||||
void
|
||||
ShmemCreate(unsigned int key, unsigned int size)
|
||||
{
|
||||
if (size)
|
||||
ShmemSize = size;
|
||||
/* create shared mem region */
|
||||
if ((ShmemId=IpcMemoryCreate(key,ShmemSize,IPCProtection))
|
||||
==IpcMemCreationFailed) {
|
||||
elog(FATAL,"ShmemCreate: cannot create region");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* ShmemBootstrap is true if shared memory has been
|
||||
* created, but not yet initialized. Only the
|
||||
* postmaster/creator-of-all-things should have
|
||||
* this flag set.
|
||||
*/
|
||||
ShmemBootstrap = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* InitShmem() -- map region into process address space
|
||||
* and initialize shared data structures.
|
||||
*
|
||||
*/
|
||||
int
|
||||
InitShmem(unsigned int key, unsigned int size)
|
||||
{
|
||||
Pointer sharedRegion;
|
||||
unsigned long currFreeSpace;
|
||||
|
||||
HASHCTL info;
|
||||
int hash_flags;
|
||||
BindingEnt * result,item;
|
||||
bool found;
|
||||
IpcMemoryId shmid;
|
||||
|
||||
/* if zero key, use default memory size */
|
||||
if (size)
|
||||
ShmemSize = size;
|
||||
|
||||
/* default key is 0 */
|
||||
|
||||
/* attach to shared memory region (SysV or BSD OS specific) */
|
||||
if (ShmemBootstrap && key == PrivateIPCKey)
|
||||
/* if we are running backend alone */
|
||||
shmid = ShmemId;
|
||||
else
|
||||
shmid = IpcMemoryIdGet(IPCKeyGetBufferMemoryKey(key), ShmemSize);
|
||||
sharedRegion = IpcMemoryAttach(shmid);
|
||||
if (sharedRegion == NULL) {
|
||||
elog(FATAL,"AttachSharedRegion: couldn't attach to shmem\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* get pointers to the dimensions of shared memory */
|
||||
ShmemBase = (unsigned long) sharedRegion;
|
||||
ShmemEnd = (unsigned long) sharedRegion + ShmemSize;
|
||||
currFreeSpace = 0;
|
||||
|
||||
/* First long in shared memory is the count of available space */
|
||||
ShmemFreeStart = (unsigned long *) ShmemBase;
|
||||
/* next is a shmem pointer to the binding table */
|
||||
ShmemBindingTabOffset = ShmemFreeStart + 1;
|
||||
|
||||
currFreeSpace +=
|
||||
sizeof(ShmemFreeStart) + sizeof(ShmemBindingTabOffset);
|
||||
|
||||
/* bootstrap initialize spin locks so we can start to use the
|
||||
* allocator and binding table.
|
||||
*/
|
||||
if (! InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key))) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* We have just allocated additional space for two spinlocks.
|
||||
* Now setup the global free space count
|
||||
*/
|
||||
if (ShmemBootstrap) {
|
||||
*ShmemFreeStart = currFreeSpace;
|
||||
}
|
||||
|
||||
/* if ShmemFreeStart is NULL, then the allocator won't work */
|
||||
Assert(*ShmemFreeStart);
|
||||
|
||||
/* create OR attach to the shared memory binding table */
|
||||
info.keysize = BTABLE_KEYSIZE;
|
||||
info.datasize = BTABLE_DATASIZE;
|
||||
hash_flags = (HASH_ELEM);
|
||||
|
||||
/* This will acquire the binding table lock, but not release it. */
|
||||
BindingTable = ShmemInitHash("BindingTable",
|
||||
BTABLE_SIZE,BTABLE_SIZE,
|
||||
&info,hash_flags);
|
||||
|
||||
if (! BindingTable) {
|
||||
elog(FATAL,"InitShmem: couldn't initialize Binding Table");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* Now, check the binding table for an entry to the binding
|
||||
* table. If there is an entry there, someone else created
|
||||
* the table. Otherwise, we did and we have to initialize it.
|
||||
*/
|
||||
memset(item.key, 0, BTABLE_KEYSIZE);
|
||||
strncpy(item.key,"BindingTable",BTABLE_KEYSIZE);
|
||||
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item,HASH_ENTER, &found);
|
||||
|
||||
|
||||
if (! result ) {
|
||||
elog(FATAL,"InitShmem: corrupted binding table");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if (! found) {
|
||||
/* bootstrapping shmem: we have to initialize the
|
||||
* binding table now.
|
||||
*/
|
||||
|
||||
Assert(ShmemBootstrap);
|
||||
result->location = MAKE_OFFSET(BindingTable->hctl);
|
||||
*ShmemBindingTabOffset = result->location;
|
||||
result->size = BTABLE_SIZE;
|
||||
|
||||
ShmemBootstrap = FALSE;
|
||||
|
||||
} else {
|
||||
Assert(! ShmemBootstrap);
|
||||
}
|
||||
/* now release the lock acquired in ShmemHashInit */
|
||||
SpinRelease (BindingLock);
|
||||
|
||||
Assert (result->location == MAKE_OFFSET(BindingTable->hctl));
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemAlloc -- allocate word-aligned byte string from
|
||||
* shared memory
|
||||
*
|
||||
* Assumes ShmemLock and ShmemFreeStart are initialized.
|
||||
* Returns: real pointer to memory or NULL if we are out
|
||||
* of space. Has to return a real pointer in order
|
||||
* to be compatable with malloc().
|
||||
*/
|
||||
long *
|
||||
ShmemAlloc(unsigned long size)
|
||||
{
|
||||
unsigned long tmpFree;
|
||||
long *newSpace;
|
||||
|
||||
/*
|
||||
* ensure space is word aligned.
|
||||
*
|
||||
* Word-alignment is not good enough. We have to be more
|
||||
* conservative: doubles need 8-byte alignment. (We probably only need
|
||||
* this on RISC platforms but this is not a big waste of space.)
|
||||
* - ay 12/94
|
||||
*/
|
||||
if (size % sizeof(double))
|
||||
size += sizeof(double) - (size % sizeof(double));
|
||||
|
||||
Assert(*ShmemFreeStart);
|
||||
|
||||
SpinAcquire(ShmemLock);
|
||||
|
||||
tmpFree = *ShmemFreeStart + size;
|
||||
if (tmpFree <= ShmemSize) {
|
||||
newSpace = (long *)MAKE_PTR(*ShmemFreeStart);
|
||||
*ShmemFreeStart += size;
|
||||
} else {
|
||||
newSpace = NULL;
|
||||
}
|
||||
|
||||
SpinRelease(ShmemLock);
|
||||
|
||||
if (! newSpace) {
|
||||
elog(NOTICE,"ShmemAlloc: out of memory ");
|
||||
}
|
||||
return(newSpace);
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemIsValid -- test if an offset refers to valid shared memory
|
||||
*
|
||||
* Returns TRUE if the pointer is valid.
|
||||
*/
|
||||
int
|
||||
ShmemIsValid(unsigned long addr)
|
||||
{
|
||||
return ((addr<ShmemEnd) && (addr>=ShmemBase));
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemInitHash -- Create/Attach to and initialize
|
||||
* shared memory hash table.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* assume caller is doing some kind of synchronization
|
||||
* so that two people dont try to create/initialize the
|
||||
* table at once. Use SpinAlloc() to create a spinlock
|
||||
* for the structure before creating the structure itself.
|
||||
*/
|
||||
HTAB *
|
||||
ShmemInitHash(char *name, /* table string name for binding */
|
||||
long init_size, /* initial size */
|
||||
long max_size, /* max size of the table */
|
||||
HASHCTL *infoP, /* info about key and bucket size */
|
||||
int hash_flags) /* info about infoP */
|
||||
{
|
||||
bool found;
|
||||
long * location;
|
||||
|
||||
/* shared memory hash tables have a fixed max size so that the
|
||||
* control structures don't try to grow. The segbase is for
|
||||
* calculating pointer values. The shared memory allocator
|
||||
* must be specified.
|
||||
*/
|
||||
infoP->segbase = (long *) ShmemBase;
|
||||
infoP->alloc = ShmemAlloc;
|
||||
infoP->max_size = max_size;
|
||||
hash_flags |= HASH_SHARED_MEM;
|
||||
|
||||
/* look it up in the binding table */
|
||||
location =
|
||||
ShmemInitStruct(name,my_log2(max_size) + sizeof(HHDR),&found);
|
||||
|
||||
/* binding table is corrupted. Let someone else give the
|
||||
* error message since they have more information
|
||||
*/
|
||||
if (location == NULL) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* it already exists, attach to it rather than allocate and
|
||||
* initialize new space
|
||||
*/
|
||||
if (found) {
|
||||
hash_flags |= HASH_ATTACH;
|
||||
}
|
||||
|
||||
/* these structures were allocated or bound in ShmemInitStruct */
|
||||
/* control information and parameters */
|
||||
infoP->hctl = (long *) location;
|
||||
/* directory for hash lookup */
|
||||
infoP->dir = (long *) (location + sizeof(HHDR));
|
||||
|
||||
return(hash_create(init_size, infoP, hash_flags));;
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemPIDLookup -- lookup process data structure using process id
|
||||
*
|
||||
* Returns: TRUE if no error. locationPtr is initialized if PID is
|
||||
* found in the binding table.
|
||||
*
|
||||
* NOTES:
|
||||
* only information about success or failure is the value of
|
||||
* locationPtr.
|
||||
*/
|
||||
bool
|
||||
ShmemPIDLookup(int pid, SHMEM_OFFSET* locationPtr)
|
||||
{
|
||||
BindingEnt * result,item;
|
||||
bool found;
|
||||
|
||||
Assert (BindingTable);
|
||||
memset(item.key, 0, BTABLE_KEYSIZE);
|
||||
sprintf(item.key,"PID %d",pid);
|
||||
|
||||
SpinAcquire(BindingLock);
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item, HASH_ENTER, &found);
|
||||
|
||||
if (! result) {
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
elog(WARN,"ShmemInitPID: BindingTable corrupted");
|
||||
return(FALSE);
|
||||
|
||||
}
|
||||
|
||||
if (found) {
|
||||
*locationPtr = result->location;
|
||||
} else {
|
||||
result->location = *locationPtr;
|
||||
}
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemPIDDestroy -- destroy binding table entry for process
|
||||
* using process id
|
||||
*
|
||||
* Returns: offset of the process struct in shared memory or
|
||||
* INVALID_OFFSET if not found.
|
||||
*
|
||||
* Side Effect: removes the entry from the binding table
|
||||
*/
|
||||
SHMEM_OFFSET
|
||||
ShmemPIDDestroy(int pid)
|
||||
{
|
||||
BindingEnt * result,item;
|
||||
bool found;
|
||||
SHMEM_OFFSET location;
|
||||
|
||||
Assert(BindingTable);
|
||||
|
||||
memset(item.key, 0, BTABLE_KEYSIZE);
|
||||
sprintf(item.key,"PID %d",pid);
|
||||
|
||||
SpinAcquire(BindingLock);
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item, HASH_REMOVE, &found);
|
||||
|
||||
if (found)
|
||||
location = result->location;
|
||||
SpinRelease(BindingLock);
|
||||
|
||||
if (! result) {
|
||||
|
||||
elog(WARN,"ShmemPIDDestroy: PID table corrupted");
|
||||
return(INVALID_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
if (found)
|
||||
return (location);
|
||||
else {
|
||||
return(INVALID_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ShmemInitStruct -- Create/attach to a structure in shared
|
||||
* memory.
|
||||
*
|
||||
* This is called during initialization to find or allocate
|
||||
* a data structure in shared memory. If no other processes
|
||||
* have created the structure, this routine allocates space
|
||||
* for it. If it exists already, a pointer to the existing
|
||||
* table is returned.
|
||||
*
|
||||
* Returns: real pointer to the object. FoundPtr is TRUE if
|
||||
* the object is already in the binding table (hence, already
|
||||
* initialized).
|
||||
*/
|
||||
long *
|
||||
ShmemInitStruct(char *name, unsigned long size, bool *foundPtr)
|
||||
{
|
||||
BindingEnt * result,item;
|
||||
long * structPtr;
|
||||
|
||||
strncpy(item.key,name,BTABLE_KEYSIZE);
|
||||
item.location = BAD_LOCATION;
|
||||
|
||||
SpinAcquire(BindingLock);
|
||||
|
||||
if (! BindingTable) {
|
||||
/* Assert() is a macro now. substitutes inside quotes. */
|
||||
char *strname = "BindingTable";
|
||||
|
||||
/* If the binding table doesnt exist, we fake it.
|
||||
*
|
||||
* If we are creating the first binding table, then let
|
||||
* shmemalloc() allocate the space for a new HTAB. Otherwise,
|
||||
* find the old one and return that. Notice that the
|
||||
* BindingLock is held until the binding table has been completely
|
||||
* initialized.
|
||||
*/
|
||||
Assert (! strcmp(name,strname)) ;
|
||||
if (ShmemBootstrap) {
|
||||
/* in POSTMASTER/Single process */
|
||||
|
||||
*foundPtr = FALSE;
|
||||
return((long *)ShmemAlloc(size));
|
||||
|
||||
} else {
|
||||
Assert (ShmemBindingTabOffset);
|
||||
|
||||
*foundPtr = TRUE;
|
||||
return((long *)MAKE_PTR(*ShmemBindingTabOffset));
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
/* look it up in the bindint table */
|
||||
result = (BindingEnt *)
|
||||
hash_search(BindingTable,(char *) &item,HASH_ENTER, foundPtr);
|
||||
}
|
||||
|
||||
if (! result) {
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
|
||||
elog(WARN,"ShmemInitStruct: Binding Table corrupted");
|
||||
return(NULL);
|
||||
|
||||
} else if (*foundPtr) {
|
||||
/*
|
||||
* Structure is in the binding table so someone else has allocated
|
||||
* it already. The size better be the same as the size we are
|
||||
* trying to initialize to or there is a name conflict (or worse).
|
||||
*/
|
||||
if (result->size != size) {
|
||||
SpinRelease(BindingLock);
|
||||
|
||||
elog(NOTICE,"ShmemInitStruct: BindingTable entry size is wrong");
|
||||
/* let caller print its message too */
|
||||
return(NULL);
|
||||
}
|
||||
structPtr = (long *)MAKE_PTR(result->location);
|
||||
} else {
|
||||
|
||||
/* It isn't in the table yet. allocate and initialize it */
|
||||
structPtr = ShmemAlloc((long)size);
|
||||
if (! structPtr) {
|
||||
/* out of memory */
|
||||
Assert (BindingTable);
|
||||
(void) hash_search(BindingTable,(char *) &item,HASH_REMOVE, foundPtr);
|
||||
SpinRelease(BindingLock);
|
||||
*foundPtr = FALSE;
|
||||
|
||||
elog(NOTICE,"ShmemInitStruct: cannot allocate '%s'",
|
||||
name);
|
||||
return(NULL);
|
||||
}
|
||||
result->size = size;
|
||||
result->location = MAKE_OFFSET(structPtr);
|
||||
}
|
||||
Assert (ShmemIsValid((unsigned long)structPtr));
|
||||
|
||||
SpinRelease(BindingLock);
|
||||
return(structPtr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
251
src/backend/storage/ipc/shmqueue.c
Normal file
251
src/backend/storage/ipc/shmqueue.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* shmqueue.c--
|
||||
* shared memory linked lists
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmqueue.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* Package for managing doubly-linked lists in shared memory.
|
||||
* The only tricky thing is that SHM_QUEUE will usually be a field
|
||||
* in a larger record. SHMQueueGetFirst has to return a pointer
|
||||
* to the record itself instead of a pointer to the SHMQueue field
|
||||
* of the record. It takes an extra pointer and does some extra
|
||||
* pointer arithmetic to do this correctly.
|
||||
*
|
||||
* NOTE: These are set up so they can be turned into macros some day.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include "postgres.h"
|
||||
#include "storage/shmem.h" /* where the declarations go */
|
||||
#include "utils/elog.h"
|
||||
|
||||
/*#define SHMQUEUE_DEBUG*/
|
||||
#ifdef SHMQUEUE_DEBUG
|
||||
#define SHMQUEUE_DEBUG_DEL /* deletions */
|
||||
#define SHMQUEUE_DEBUG_HD /* head inserts */
|
||||
#define SHMQUEUE_DEBUG_TL /* tail inserts */
|
||||
#define SHMQUEUE_DEBUG_ELOG NOTICE
|
||||
#endif /* SHMQUEUE_DEBUG */
|
||||
|
||||
/*
|
||||
* ShmemQueueInit -- make the head of a new queue point
|
||||
* to itself
|
||||
*/
|
||||
void
|
||||
SHMQueueInit(SHM_QUEUE *queue)
|
||||
{
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
(queue)->prev = (queue)->next = MAKE_OFFSET(queue);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHMQueueIsDetached -- TRUE if element is not currently
|
||||
* in a queue.
|
||||
*/
|
||||
bool
|
||||
SHMQueueIsDetached(SHM_QUEUE *queue)
|
||||
{
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
return ((queue)->prev == INVALID_OFFSET);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHMQueueElemInit -- clear an element's links
|
||||
*/
|
||||
void
|
||||
SHMQueueElemInit(SHM_QUEUE *queue)
|
||||
{
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
(queue)->prev = (queue)->next = INVALID_OFFSET;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHMQueueDelete -- remove an element from the queue and
|
||||
* close the links
|
||||
*/
|
||||
void
|
||||
SHMQueueDelete(SHM_QUEUE *queue)
|
||||
{
|
||||
SHM_QUEUE *nextElem = (SHM_QUEUE *) MAKE_PTR((queue)->next);
|
||||
SHM_QUEUE *prevElem = (SHM_QUEUE *) MAKE_PTR((queue)->prev);
|
||||
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
Assert(SHM_PTR_VALID(nextElem));
|
||||
Assert(SHM_PTR_VALID(prevElem));
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG_DEL
|
||||
dumpQ(queue, "in SHMQueueDelete: begin");
|
||||
#endif /* SHMQUEUE_DEBUG_DEL */
|
||||
|
||||
prevElem->next = (queue)->next;
|
||||
nextElem->prev = (queue)->prev;
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG_DEL
|
||||
dumpQ((SHM_QUEUE *)MAKE_PTR(queue->prev), "in SHMQueueDelete: end");
|
||||
#endif /* SHMQUEUE_DEBUG_DEL */
|
||||
}
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG
|
||||
void
|
||||
dumpQ(SHM_QUEUE *q, char *s)
|
||||
{
|
||||
char elem[16];
|
||||
char buf[1024];
|
||||
SHM_QUEUE *start = q;
|
||||
int count = 0;
|
||||
|
||||
sprintf(buf, "q prevs: %x", MAKE_OFFSET(q));
|
||||
q = (SHM_QUEUE *)MAKE_PTR(q->prev);
|
||||
while (q != start)
|
||||
{
|
||||
sprintf(elem, "--->%x", MAKE_OFFSET(q));
|
||||
strcat(buf, elem);
|
||||
q = (SHM_QUEUE *)MAKE_PTR(q->prev);
|
||||
if (q->prev == MAKE_OFFSET(q))
|
||||
break;
|
||||
if (count++ > 40)
|
||||
{
|
||||
strcat(buf, "BAD PREV QUEUE!!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
sprintf(elem, "--->%x", MAKE_OFFSET(q));
|
||||
strcat(buf, elem);
|
||||
elog(SHMQUEUE_DEBUG_ELOG, "%s: %s", s, buf);
|
||||
|
||||
sprintf(buf, "q nexts: %x", MAKE_OFFSET(q));
|
||||
count = 0;
|
||||
q = (SHM_QUEUE *)MAKE_PTR(q->next);
|
||||
while (q != start)
|
||||
{
|
||||
sprintf(elem, "--->%x", MAKE_OFFSET(q));
|
||||
strcat(buf, elem);
|
||||
q = (SHM_QUEUE *)MAKE_PTR(q->next);
|
||||
if (q->next == MAKE_OFFSET(q))
|
||||
break;
|
||||
if (count++ > 10)
|
||||
{
|
||||
strcat(buf, "BAD NEXT QUEUE!!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
sprintf(elem, "--->%x", MAKE_OFFSET(q));
|
||||
strcat(buf, elem);
|
||||
elog(SHMQUEUE_DEBUG_ELOG, "%s: %s", s, buf);
|
||||
}
|
||||
#endif /* SHMQUEUE_DEBUG */
|
||||
|
||||
/*
|
||||
* SHMQueueInsertHD -- put elem in queue between the queue head
|
||||
* and its "prev" element.
|
||||
*/
|
||||
void
|
||||
SHMQueueInsertHD(SHM_QUEUE *queue, SHM_QUEUE *elem)
|
||||
{
|
||||
SHM_QUEUE *prevPtr = (SHM_QUEUE *) MAKE_PTR((queue)->prev);
|
||||
SHMEM_OFFSET elemOffset = MAKE_OFFSET(elem);
|
||||
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
Assert(SHM_PTR_VALID(elem));
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG_HD
|
||||
dumpQ(queue, "in SHMQueueInsertHD: begin");
|
||||
#endif /* SHMQUEUE_DEBUG_HD */
|
||||
|
||||
(elem)->next = prevPtr->next;
|
||||
(elem)->prev = queue->prev;
|
||||
(queue)->prev = elemOffset;
|
||||
prevPtr->next = elemOffset;
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG_HD
|
||||
dumpQ(queue, "in SHMQueueInsertHD: end");
|
||||
#endif /* SHMQUEUE_DEBUG_HD */
|
||||
}
|
||||
|
||||
void
|
||||
SHMQueueInsertTL(SHM_QUEUE *queue, SHM_QUEUE *elem)
|
||||
{
|
||||
SHM_QUEUE *nextPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next);
|
||||
SHMEM_OFFSET elemOffset = MAKE_OFFSET(elem);
|
||||
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
Assert(SHM_PTR_VALID(elem));
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG_TL
|
||||
dumpQ(queue, "in SHMQueueInsertTL: begin");
|
||||
#endif /* SHMQUEUE_DEBUG_TL */
|
||||
|
||||
(elem)->prev = nextPtr->prev;
|
||||
(elem)->next = queue->next;
|
||||
(queue)->next = elemOffset;
|
||||
nextPtr->prev = elemOffset;
|
||||
|
||||
#ifdef SHMQUEUE_DEBUG_TL
|
||||
dumpQ(queue, "in SHMQueueInsertTL: end");
|
||||
#endif /* SHMQUEUE_DEBUG_TL */
|
||||
}
|
||||
|
||||
/*
|
||||
* SHMQueueFirst -- Get the first element from a queue
|
||||
*
|
||||
* First element is queue->next. If SHMQueue is part of
|
||||
* a larger structure, we want to return a pointer to the
|
||||
* whole structure rather than a pointer to its SHMQueue field.
|
||||
* I.E. struct {
|
||||
* int stuff;
|
||||
* SHMQueue elem;
|
||||
* } ELEMType;
|
||||
* when this element is in a queue (queue->next) is struct.elem.
|
||||
* nextQueue allows us to calculate the offset of the SHMQueue
|
||||
* field in the structure.
|
||||
*
|
||||
* call to SHMQueueFirst should take these parameters:
|
||||
*
|
||||
* &(queueHead),&firstElem,&(firstElem->next)
|
||||
*
|
||||
* Note that firstElem may well be uninitialized. if firstElem
|
||||
* is initially K, &(firstElem->next) will be K+ the offset to
|
||||
* next.
|
||||
*/
|
||||
void
|
||||
SHMQueueFirst(SHM_QUEUE *queue, Pointer *nextPtrPtr, SHM_QUEUE *nextQueue)
|
||||
{
|
||||
SHM_QUEUE *elemPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next);
|
||||
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
*nextPtrPtr = (Pointer) (((unsigned long) *nextPtrPtr) +
|
||||
((unsigned long) elemPtr) - ((unsigned long) nextQueue));
|
||||
|
||||
/*
|
||||
nextPtrPtr a ptr to a structure linked in the queue
|
||||
nextQueue is the SHMQueue field of the structure
|
||||
*nextPtrPtr - nextQueue is 0 minus the offset of the queue
|
||||
field n the record
|
||||
elemPtr + (*nextPtrPtr - nexQueue) is the start of the
|
||||
structure containing elemPtr.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* SHMQueueEmpty -- TRUE if queue head is only element, FALSE otherwise
|
||||
*/
|
||||
bool
|
||||
SHMQueueEmpty(SHM_QUEUE *queue)
|
||||
{
|
||||
Assert(SHM_PTR_VALID(queue));
|
||||
|
||||
if (queue->prev == MAKE_OFFSET(queue))
|
||||
{
|
||||
Assert(queue->next = MAKE_OFFSET(queue));
|
||||
return(TRUE);
|
||||
}
|
||||
return(FALSE);
|
||||
}
|
||||
169
src/backend/storage/ipc/sinval.c
Normal file
169
src/backend/storage/ipc/sinval.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* sinval.c--
|
||||
* POSTGRES shared cache invalidation communication code.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/* #define INVALIDDEBUG 1 */
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "storage/sinval.h"
|
||||
#include "storage/sinvaladt.h"
|
||||
#include "storage/spin.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
extern SISeg *shmInvalBuffer;/* the shared buffer segment, set by*/
|
||||
/* SISegmentAttach() */
|
||||
extern BackendId MyBackendId;
|
||||
extern BackendTag MyBackendTag;
|
||||
|
||||
SPINLOCK SInvalLock = (SPINLOCK) NULL;
|
||||
|
||||
/****************************************************************************/
|
||||
/* CreateSharedInvalidationState(key) Create a buffer segment */
|
||||
/* */
|
||||
/* should be called only by the POSTMASTER */
|
||||
/****************************************************************************/
|
||||
void
|
||||
CreateSharedInvalidationState(IPCKey key)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* REMOVED
|
||||
SISyncKill(IPCKeyGetSIBufferMemorySemaphoreKey(key));
|
||||
SISyncInit(IPCKeyGetSIBufferMemorySemaphoreKey(key));
|
||||
*/
|
||||
|
||||
/* SInvalLock gets set in spin.c, during spinlock init */
|
||||
status = SISegmentInit(true, IPCKeyGetSIBufferMemoryBlock(key));
|
||||
|
||||
if (status == -1) {
|
||||
elog(FATAL, "CreateSharedInvalidationState: failed segment init");
|
||||
}
|
||||
}
|
||||
/****************************************************************************/
|
||||
/* AttachSharedInvalidationState(key) Attach a buffer segment */
|
||||
/* */
|
||||
/* should be called only by the POSTMASTER */
|
||||
/****************************************************************************/
|
||||
void
|
||||
AttachSharedInvalidationState(IPCKey key)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (key == PrivateIPCKey) {
|
||||
CreateSharedInvalidationState(key);
|
||||
return;
|
||||
}
|
||||
/* SInvalLock gets set in spin.c, during spinlock init */
|
||||
status = SISegmentInit(false, IPCKeyGetSIBufferMemoryBlock(key));
|
||||
|
||||
if (status == -1) {
|
||||
elog(FATAL, "AttachSharedInvalidationState: failed segment init");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InitSharedInvalidationState()
|
||||
{
|
||||
SpinAcquire(SInvalLock);
|
||||
if (!SIBackendInit(shmInvalBuffer))
|
||||
{
|
||||
SpinRelease(SInvalLock);
|
||||
elog(FATAL, "Backend cache invalidation initialization failed");
|
||||
}
|
||||
SpinRelease(SInvalLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* RegisterSharedInvalid --
|
||||
* Returns a new local cache invalidation state containing a new entry.
|
||||
*
|
||||
* Note:
|
||||
* Assumes hash index is valid.
|
||||
* Assumes item pointer is valid.
|
||||
*/
|
||||
/****************************************************************************/
|
||||
/* RegisterSharedInvalid(cacheId, hashIndex, pointer) */
|
||||
/* */
|
||||
/* register a message in the buffer */
|
||||
/* should be called by a backend */
|
||||
/****************************************************************************/
|
||||
void
|
||||
RegisterSharedInvalid(int cacheId, /* XXX */
|
||||
Index hashIndex,
|
||||
ItemPointer pointer)
|
||||
{
|
||||
SharedInvalidData newInvalid;
|
||||
|
||||
/*
|
||||
* This code has been hacked to accept two types of messages. This might
|
||||
* be treated more generally in the future.
|
||||
*
|
||||
* (1)
|
||||
* cacheId= system cache id
|
||||
* hashIndex= system cache hash index for a (possibly) cached tuple
|
||||
* pointer= pointer of (possibly) cached tuple
|
||||
*
|
||||
* (2)
|
||||
* cacheId= special non-syscache id
|
||||
* hashIndex= object id contained in (possibly) cached relation descriptor
|
||||
* pointer= null
|
||||
*/
|
||||
|
||||
newInvalid.cacheId = cacheId;
|
||||
newInvalid.hashIndex = hashIndex;
|
||||
|
||||
if (ItemPointerIsValid(pointer)) {
|
||||
ItemPointerCopy(pointer, &newInvalid.pointerData);
|
||||
} else {
|
||||
ItemPointerSetInvalid(&newInvalid.pointerData);
|
||||
}
|
||||
|
||||
SpinAcquire(SInvalLock);
|
||||
if (!SISetDataEntry(shmInvalBuffer, &newInvalid)) {
|
||||
/* buffer full */
|
||||
/* release a message, mark process cache states to be invalid */
|
||||
SISetProcStateInvalid(shmInvalBuffer);
|
||||
|
||||
if (!SIDelDataEntry(shmInvalBuffer)) {
|
||||
/* inconsistent buffer state -- shd never happen */
|
||||
SpinRelease(SInvalLock);
|
||||
elog(FATAL, "RegisterSharedInvalid: inconsistent buffer state");
|
||||
}
|
||||
|
||||
/* write again */
|
||||
(void) SISetDataEntry(shmInvalBuffer, &newInvalid);
|
||||
}
|
||||
SpinRelease(SInvalLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* InvalidateSharedInvalid --
|
||||
* Processes all entries in a shared cache invalidation state.
|
||||
*/
|
||||
/****************************************************************************/
|
||||
/* InvalidateSharedInvalid(invalFunction, resetFunction) */
|
||||
/* */
|
||||
/* invalidate a message in the buffer (read and clean up) */
|
||||
/* should be called by a backend */
|
||||
/****************************************************************************/
|
||||
void
|
||||
InvalidateSharedInvalid(void (*invalFunction)(),
|
||||
void (*resetFunction)())
|
||||
{
|
||||
SpinAcquire(SInvalLock);
|
||||
SIReadEntryData(shmInvalBuffer, MyBackendId,
|
||||
invalFunction, resetFunction);
|
||||
|
||||
SIDelExpiredDataEntries(shmInvalBuffer);
|
||||
SpinRelease(SInvalLock);
|
||||
}
|
||||
797
src/backend/storage/ipc/sinvaladt.c
Normal file
797
src/backend/storage/ipc/sinvaladt.c
Normal file
@@ -0,0 +1,797 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* sinvaladt.c--
|
||||
* POSTGRES shared cache invalidation segment definitions.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/sinvaladt.h"
|
||||
#include "storage/lmgr.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
/* ----------------
|
||||
* global variable notes
|
||||
*
|
||||
* SharedInvalidationSemaphore
|
||||
*
|
||||
* shmInvalBuffer
|
||||
* the shared buffer segment, set by SISegmentAttach()
|
||||
*
|
||||
* MyBackendId
|
||||
* might be removed later, used only for
|
||||
* debugging in debug routines (end of file)
|
||||
*
|
||||
* SIDbId
|
||||
* identification of buffer (disappears)
|
||||
*
|
||||
* SIRelId \
|
||||
* SIDummyOid \ identification of buffer
|
||||
* SIXidData /
|
||||
* SIXid /
|
||||
*
|
||||
* XXX This file really needs to be cleaned up. We switched to using
|
||||
* spinlocks to protect critical sections (as opposed to using fake
|
||||
* relations and going through the lock manager) and some of the old
|
||||
* cruft was 'ifdef'ed out, while other parts (now unused) are still
|
||||
* compiled into the system. -mer 5/24/92
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef HAS_TEST_AND_SET
|
||||
int SharedInvalidationLockId;
|
||||
#else
|
||||
IpcSemaphoreId SharedInvalidationSemaphore;
|
||||
#endif
|
||||
|
||||
SISeg *shmInvalBuffer;
|
||||
extern BackendId MyBackendId;
|
||||
|
||||
static void CleanupInvalidationState(int status, SISeg *segInOutP);
|
||||
static BackendId SIAssignBackendId(SISeg *segInOutP, BackendTag backendTag);
|
||||
static int SIGetNumEntries(SISeg *segP);
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetActiveProcess(segP, backendId) set the backend status active */
|
||||
/* should be called only by the postmaster when creating a backend */
|
||||
/************************************************************************/
|
||||
/* XXX I suspect that the segP parameter is extraneous. -hirohama */
|
||||
static void
|
||||
SISetActiveProcess(SISeg *segInOutP, BackendId backendId)
|
||||
{
|
||||
/* mark all messages as read */
|
||||
|
||||
/* Assert(segP->procState[backendId - 1].tag == MyBackendTag); */
|
||||
|
||||
segInOutP->procState[backendId - 1].resetState = false;
|
||||
segInOutP->procState[backendId - 1].limit = SIGetNumEntries(segInOutP);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* SIBackendInit() initializes a backend to operate on the buffer */
|
||||
/****************************************************************************/
|
||||
int
|
||||
SIBackendInit(SISeg *segInOutP)
|
||||
{
|
||||
LRelId LtCreateRelId();
|
||||
TransactionId LMITransactionIdCopy();
|
||||
|
||||
Assert(MyBackendTag > 0);
|
||||
|
||||
MyBackendId = SIAssignBackendId(segInOutP, MyBackendTag);
|
||||
if (MyBackendId == InvalidBackendTag)
|
||||
return 0;
|
||||
|
||||
#ifdef INVALIDDEBUG
|
||||
elog(DEBUG, "SIBackendInit: backend tag %d; backend id %d.",
|
||||
MyBackendTag, MyBackendId);
|
||||
#endif /* INVALIDDEBUG */
|
||||
|
||||
SISetActiveProcess(segInOutP, MyBackendId);
|
||||
on_exitpg(CleanupInvalidationState, (caddr_t)segInOutP);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* SIAssignBackendId
|
||||
* ----------------
|
||||
*/
|
||||
static BackendId
|
||||
SIAssignBackendId(SISeg *segInOutP, BackendTag backendTag)
|
||||
{
|
||||
Index index;
|
||||
ProcState *stateP;
|
||||
|
||||
stateP = NULL;
|
||||
|
||||
for (index = 0; index < MaxBackendId; index += 1) {
|
||||
if (segInOutP->procState[index].tag == InvalidBackendTag ||
|
||||
segInOutP->procState[index].tag == backendTag)
|
||||
{
|
||||
stateP = &segInOutP->procState[index];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!PointerIsValid(stateP) ||
|
||||
(segInOutP->procState[index].resetState &&
|
||||
(!stateP->resetState ||
|
||||
stateP->tag < backendTag)) ||
|
||||
(!stateP->resetState &&
|
||||
(segInOutP->procState[index].limit <
|
||||
stateP->limit ||
|
||||
stateP->tag < backendTag)))
|
||||
{
|
||||
stateP = &segInOutP->procState[index];
|
||||
}
|
||||
}
|
||||
|
||||
/* verify that all "procState" entries checked for matching tags */
|
||||
|
||||
for (index += 1; index < MaxBackendId; index += 1) {
|
||||
if (segInOutP->procState[index].tag == backendTag) {
|
||||
elog (FATAL, "SIAssignBackendId: tag %d found twice",
|
||||
backendTag);
|
||||
}
|
||||
}
|
||||
|
||||
if (stateP->tag != InvalidBackendTag) {
|
||||
if (stateP->tag == backendTag) {
|
||||
elog(NOTICE, "SIAssignBackendId: reusing tag %d",
|
||||
backendTag);
|
||||
} else {
|
||||
elog(NOTICE,
|
||||
"SIAssignBackendId: discarding tag %d",
|
||||
stateP->tag);
|
||||
return InvalidBackendTag;
|
||||
}
|
||||
}
|
||||
|
||||
stateP->tag = backendTag;
|
||||
|
||||
return (1 + stateP - &segInOutP->procState[0]);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* The following function should be called only by the postmaster !! */
|
||||
/************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetDeadProcess(segP, backendId) set the backend status DEAD */
|
||||
/* should be called only by the postmaster when a backend died */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISetDeadProcess(SISeg *segP, int backendId)
|
||||
{
|
||||
/* XXX call me.... */
|
||||
|
||||
segP->procState[backendId - 1].resetState = false;
|
||||
segP->procState[backendId - 1].limit = -1;
|
||||
segP->procState[backendId - 1].tag = InvalidBackendTag;
|
||||
}
|
||||
|
||||
/*
|
||||
* CleanupInvalidationState --
|
||||
* Note:
|
||||
* This is a temporary hack. ExitBackend should call this instead
|
||||
* of exit (via on_exitpg).
|
||||
*/
|
||||
static void
|
||||
CleanupInvalidationState(int status, /* XXX */
|
||||
SISeg *segInOutP) /* XXX style */
|
||||
{
|
||||
Assert(PointerIsValid(segInOutP));
|
||||
|
||||
SISetDeadProcess(segInOutP, MyBackendId);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIComputeSize() - retuns the size of a buffer segment */
|
||||
/************************************************************************/
|
||||
static SISegOffsets *
|
||||
SIComputeSize(int *segSize)
|
||||
{
|
||||
int A, B, a, b, totalSize;
|
||||
SISegOffsets *oP;
|
||||
|
||||
A = 0;
|
||||
a = SizeSISeg; /* offset to first data entry */
|
||||
b = SizeOfOneSISegEntry * MAXNUMMESSAGES;
|
||||
B = A + a + b;
|
||||
totalSize = B - A;
|
||||
*segSize = totalSize;
|
||||
|
||||
oP = (SISegOffsets *) palloc(sizeof(SISegOffsets));
|
||||
oP->startSegment = A;
|
||||
oP->offsetToFirstEntry = a; /* relatiove to A */
|
||||
oP->offsetToEndOfSegemnt = totalSize; /* relative to A */
|
||||
return(oP);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetStartEntrySection(segP, offset) - sets the offset */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISetStartEntrySection(SISeg *segP, Offset offset)
|
||||
{
|
||||
segP->startEntrySection = offset;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetStartEntrySection(segP) - returnss the offset */
|
||||
/************************************************************************/
|
||||
static Offset
|
||||
SIGetStartEntrySection(SISeg *segP)
|
||||
{
|
||||
return(segP->startEntrySection);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetEndEntrySection(segP, offset) - sets the offset */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISetEndEntrySection(SISeg *segP, Offset offset)
|
||||
{
|
||||
segP->endEntrySection = offset;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetEndEntryChain(segP, offset) - sets the offset */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISetEndEntryChain(SISeg *segP, Offset offset)
|
||||
{
|
||||
segP->endEntryChain = offset;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetEndEntryChain(segP) - returnss the offset */
|
||||
/************************************************************************/
|
||||
static Offset
|
||||
SIGetEndEntryChain(SISeg *segP)
|
||||
{
|
||||
return(segP->endEntryChain);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetStartEntryChain(segP, offset) - sets the offset */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISetStartEntryChain(SISeg *segP, Offset offset)
|
||||
{
|
||||
segP->startEntryChain = offset;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetStartEntryChain(segP) - returns the offset */
|
||||
/************************************************************************/
|
||||
static Offset
|
||||
SIGetStartEntryChain(SISeg *segP)
|
||||
{
|
||||
return(segP->startEntryChain);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetNumEntries(segP, num) sets the current nuber of entries */
|
||||
/************************************************************************/
|
||||
static bool
|
||||
SISetNumEntries(SISeg *segP, int num)
|
||||
{
|
||||
if ( num <= MAXNUMMESSAGES) {
|
||||
segP->numEntries = num;
|
||||
return(true);
|
||||
} else {
|
||||
return(false); /* table full */
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetNumEntries(segP) - returns the current nuber of entries */
|
||||
/************************************************************************/
|
||||
static int
|
||||
SIGetNumEntries(SISeg *segP)
|
||||
{
|
||||
return(segP->numEntries);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetMaxNumEntries(segP, num) sets the maximal number of entries */
|
||||
/************************************************************************/
|
||||
static bool
|
||||
SISetMaxNumEntries(SISeg *segP, int num)
|
||||
{
|
||||
if ( num <= MAXNUMMESSAGES) {
|
||||
segP->maxNumEntries = num;
|
||||
return(true);
|
||||
} else {
|
||||
return(false); /* wrong number */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetProcStateLimit(segP, i) returns the limit of read messages */
|
||||
/************************************************************************/
|
||||
static int
|
||||
SIGetProcStateLimit(SISeg *segP, int i)
|
||||
{
|
||||
return(segP->procState[i].limit);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIIncNumEntries(segP, num) increments the current nuber of entries */
|
||||
/************************************************************************/
|
||||
static bool
|
||||
SIIncNumEntries(SISeg *segP, int num)
|
||||
{
|
||||
if ((segP->numEntries + num) <= MAXNUMMESSAGES) {
|
||||
segP->numEntries = segP->numEntries + num;
|
||||
return(true);
|
||||
} else {
|
||||
return(false); /* table full */
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIDecNumEntries(segP, num) decrements the current nuber of entries */
|
||||
/************************************************************************/
|
||||
static bool
|
||||
SIDecNumEntries(SISeg *segP, int num)
|
||||
{
|
||||
if ((segP->numEntries - num) >= 0) {
|
||||
segP->numEntries = segP->numEntries - num;
|
||||
return(true);
|
||||
} else {
|
||||
return(false); /* not enough entries in table */
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetStartFreeSpace(segP, offset) - sets the offset */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISetStartFreeSpace(SISeg *segP, Offset offset)
|
||||
{
|
||||
segP->startFreeSpace = offset;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetStartFreeSpace(segP) - returns the offset */
|
||||
/************************************************************************/
|
||||
static Offset
|
||||
SIGetStartFreeSpace(SISeg *segP)
|
||||
{
|
||||
return(segP->startFreeSpace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetFirstDataEntry(segP) returns first data entry */
|
||||
/************************************************************************/
|
||||
static SISegEntry *
|
||||
SIGetFirstDataEntry(SISeg *segP)
|
||||
{
|
||||
SISegEntry *eP;
|
||||
Offset startChain;
|
||||
|
||||
startChain = SIGetStartEntryChain(segP);
|
||||
|
||||
if (startChain == InvalidOffset)
|
||||
return(NULL);
|
||||
|
||||
eP = (SISegEntry *) ((Pointer) segP +
|
||||
SIGetStartEntrySection(segP) +
|
||||
startChain );
|
||||
return(eP);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetLastDataEntry(segP) returns last data entry in the chain */
|
||||
/************************************************************************/
|
||||
static SISegEntry *
|
||||
SIGetLastDataEntry(SISeg *segP)
|
||||
{
|
||||
SISegEntry *eP;
|
||||
Offset endChain;
|
||||
|
||||
endChain = SIGetEndEntryChain(segP);
|
||||
|
||||
if (endChain == InvalidOffset)
|
||||
return(NULL);
|
||||
|
||||
eP = (SISegEntry *) ((Pointer) segP +
|
||||
SIGetStartEntrySection(segP) +
|
||||
endChain );
|
||||
return(eP);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetNextDataEntry(segP, offset) returns next data entry */
|
||||
/************************************************************************/
|
||||
static SISegEntry *
|
||||
SIGetNextDataEntry(SISeg *segP, Offset offset)
|
||||
{
|
||||
SISegEntry *eP;
|
||||
|
||||
if (offset == InvalidOffset)
|
||||
return(NULL);
|
||||
|
||||
eP = (SISegEntry *) ((Pointer) segP +
|
||||
SIGetStartEntrySection(segP) +
|
||||
offset);
|
||||
return(eP);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIGetNthDataEntry(segP, n) returns the n-th data entry in chain */
|
||||
/************************************************************************/
|
||||
static SISegEntry *
|
||||
SIGetNthDataEntry(SISeg *segP,
|
||||
int n) /* must range from 1 to MaxMessages */
|
||||
{
|
||||
SISegEntry *eP;
|
||||
int i;
|
||||
|
||||
if (n <= 0) return(NULL);
|
||||
|
||||
eP = SIGetFirstDataEntry(segP);
|
||||
for (i = 1; i < n; i++) {
|
||||
/* skip one and get the next */
|
||||
eP = SIGetNextDataEntry(segP, eP->next);
|
||||
}
|
||||
|
||||
return(eP);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIEntryOffset(segP, entryP) returns the offset for an pointer */
|
||||
/************************************************************************/
|
||||
static Offset
|
||||
SIEntryOffset(SISeg *segP, SISegEntry *entryP)
|
||||
{
|
||||
/* relative to B !! */
|
||||
return ((Offset) ((Pointer) entryP -
|
||||
(Pointer) segP -
|
||||
SIGetStartEntrySection(segP) ));
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetDataEntry(segP, data) - sets a message in the segemnt */
|
||||
/************************************************************************/
|
||||
bool
|
||||
SISetDataEntry(SISeg *segP, SharedInvalidData *data)
|
||||
{
|
||||
Offset offsetToNewData;
|
||||
SISegEntry *eP, *lastP;
|
||||
bool SISegFull();
|
||||
Offset SIEntryOffset();
|
||||
Offset SIGetStartFreeSpace();
|
||||
SISegEntry *SIGetFirstDataEntry();
|
||||
SISegEntry *SIGetNextDataEntry();
|
||||
SISegEntry *SIGetLastDataEntry();
|
||||
|
||||
if (!SIIncNumEntries(segP, 1))
|
||||
return(false); /* no space */
|
||||
|
||||
/* get a free entry */
|
||||
offsetToNewData = SIGetStartFreeSpace(segP);
|
||||
eP = SIGetNextDataEntry(segP, offsetToNewData); /* it's a free one */
|
||||
SISetStartFreeSpace(segP, eP->next);
|
||||
/* fill it up */
|
||||
eP->entryData = *data;
|
||||
eP->isfree = false;
|
||||
eP->next = InvalidOffset;
|
||||
|
||||
/* handle insertion point at the end of the chain !!*/
|
||||
lastP = SIGetLastDataEntry(segP);
|
||||
if (lastP == NULL) {
|
||||
/* there is no chain, insert the first entry */
|
||||
SISetStartEntryChain(segP, SIEntryOffset(segP, eP));
|
||||
} else {
|
||||
/* there is a last entry in the chain */
|
||||
lastP->next = SIEntryOffset(segP, eP);
|
||||
}
|
||||
SISetEndEntryChain(segP, SIEntryOffset(segP, eP));
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIDecProcLimit(segP, num) decrements all process limits */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SIDecProcLimit(SISeg *segP, int num)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < MaxBackendId; i++) {
|
||||
/* decrement only, if there is a limit > 0 */
|
||||
if (segP->procState[i].limit > 0) {
|
||||
segP->procState[i].limit = segP->procState[i].limit - num;
|
||||
if (segP->procState[i].limit < 0) {
|
||||
/* limit was not high enough, reset to zero */
|
||||
/* negative means it's a dead backend */
|
||||
segP->procState[i].limit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SIDelDataEntry(segP) - free the FIRST entry */
|
||||
/************************************************************************/
|
||||
bool
|
||||
SIDelDataEntry(SISeg *segP)
|
||||
{
|
||||
SISegEntry *e1P;
|
||||
SISegEntry *SIGetFirstDataEntry();
|
||||
|
||||
if (!SIDecNumEntries(segP, 1)) {
|
||||
/* no entries in buffer */
|
||||
return(false);
|
||||
}
|
||||
|
||||
e1P = SIGetFirstDataEntry(segP);
|
||||
SISetStartEntryChain(segP, e1P->next);
|
||||
if (SIGetStartEntryChain(segP) == InvalidOffset) {
|
||||
/* it was the last entry */
|
||||
SISetEndEntryChain(segP, InvalidOffset);
|
||||
}
|
||||
/* free the entry */
|
||||
e1P->isfree = true;
|
||||
e1P->next = SIGetStartFreeSpace(segP);
|
||||
SISetStartFreeSpace(segP, SIEntryOffset(segP, e1P));
|
||||
SIDecProcLimit(segP, 1);
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISetProcStateInvalid(segP) checks and marks a backends state as */
|
||||
/* invalid */
|
||||
/************************************************************************/
|
||||
void
|
||||
SISetProcStateInvalid(SISeg *segP)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < MaxBackendId; i++) {
|
||||
if (segP->procState[i].limit == 0) {
|
||||
/* backend i didn't read any message */
|
||||
segP->procState[i].resetState = true;
|
||||
/*XXX signal backend that it has to reset its internal cache ? */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIReadEntryData(segP, backendId, function) */
|
||||
/* - marks messages to be read by id */
|
||||
/* and executes function */
|
||||
/************************************************************************/
|
||||
void
|
||||
SIReadEntryData(SISeg *segP,
|
||||
int backendId,
|
||||
void (*invalFunction)(),
|
||||
void (*resetFunction)())
|
||||
{
|
||||
int i = 0;
|
||||
SISegEntry *data;
|
||||
|
||||
Assert(segP->procState[backendId - 1].tag == MyBackendTag);
|
||||
|
||||
if (!segP->procState[backendId - 1].resetState) {
|
||||
/* invalidate data, but only those, you have not seen yet !!*/
|
||||
/* therefore skip read messages */
|
||||
data = SIGetNthDataEntry(segP,
|
||||
SIGetProcStateLimit(segP, backendId - 1) + 1);
|
||||
while (data != NULL) {
|
||||
i++;
|
||||
segP->procState[backendId - 1].limit++; /* one more message read */
|
||||
invalFunction(data->entryData.cacheId,
|
||||
data->entryData.hashIndex,
|
||||
&data->entryData.pointerData);
|
||||
data = SIGetNextDataEntry(segP, data->next);
|
||||
}
|
||||
/* SIDelExpiredDataEntries(segP); */
|
||||
} else {
|
||||
/*backend must not read messages, its own state has to be reset */
|
||||
elog(NOTICE, "SIMarkEntryData: cache state reset");
|
||||
resetFunction(); /* XXXX call it here, parameters? */
|
||||
|
||||
/* new valid state--mark all messages "read" */
|
||||
segP->procState[backendId - 1].resetState = false;
|
||||
segP->procState[backendId - 1].limit = SIGetNumEntries(segP);
|
||||
}
|
||||
/* check whether we can remove dead messages */
|
||||
if (i > MAXNUMMESSAGES) {
|
||||
elog(FATAL, "SIReadEntryData: Invalid segment state");
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SIDelExpiredDataEntries (segP) - removes irrelevant messages */
|
||||
/************************************************************************/
|
||||
void
|
||||
SIDelExpiredDataEntries(SISeg *segP)
|
||||
{
|
||||
int min, i, h;
|
||||
|
||||
min = 9999999;
|
||||
for (i = 0; i < MaxBackendId; i++) {
|
||||
h = SIGetProcStateLimit(segP, i);
|
||||
if (h >= 0) { /* backend active */
|
||||
if (h < min ) min = h;
|
||||
}
|
||||
}
|
||||
if (min != 9999999) {
|
||||
/* we can remove min messages */
|
||||
for (i = 1; i <= min; i++) {
|
||||
/* this adjusts also the state limits!*/
|
||||
if (!SIDelDataEntry(segP)) {
|
||||
elog(FATAL, "SIDelExpiredDataEntries: Invalid segment state");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISegInit(segP) - initializes the segment */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISegInit(SISeg *segP)
|
||||
{
|
||||
SISegOffsets *oP;
|
||||
int segSize, i;
|
||||
SISegEntry *eP;
|
||||
|
||||
oP = SIComputeSize(&segSize);
|
||||
/* set sempahore ids in the segment */
|
||||
/* XXX */
|
||||
SISetStartEntrySection(segP, oP->offsetToFirstEntry);
|
||||
SISetEndEntrySection(segP, oP->offsetToEndOfSegemnt);
|
||||
SISetStartFreeSpace(segP, 0);
|
||||
SISetStartEntryChain(segP, InvalidOffset);
|
||||
SISetEndEntryChain(segP, InvalidOffset);
|
||||
(void) SISetNumEntries(segP, 0);
|
||||
(void) SISetMaxNumEntries(segP, MAXNUMMESSAGES);
|
||||
for (i = 0; i < MaxBackendId; i++) {
|
||||
segP->procState[i].limit = -1; /* no backend active !!*/
|
||||
segP->procState[i].resetState = false;
|
||||
segP->procState[i].tag = InvalidBackendTag;
|
||||
}
|
||||
/* construct a chain of free entries */
|
||||
for (i = 1; i < MAXNUMMESSAGES; i++) {
|
||||
eP = (SISegEntry *) ((Pointer) segP +
|
||||
SIGetStartEntrySection(segP) +
|
||||
(i - 1) * sizeof(SISegEntry));
|
||||
eP->isfree = true;
|
||||
eP->next = i * sizeof(SISegEntry); /* relative to B */
|
||||
}
|
||||
/* handle the last free entry separate */
|
||||
eP = (SISegEntry *) ((Pointer) segP +
|
||||
SIGetStartEntrySection(segP) +
|
||||
(MAXNUMMESSAGES - 1) * sizeof(SISegEntry));
|
||||
eP->isfree = true;
|
||||
eP->next = InvalidOffset; /* it's the end of the chain !! */
|
||||
/*
|
||||
* Be tidy
|
||||
*/
|
||||
pfree(oP);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISegmentKill(key) - kill any segment */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISegmentKill(int key) /* the corresponding key for the segment */
|
||||
{
|
||||
IpcMemoryKill(key);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISegmentGet(key, size) - get a shared segment of size <size> */
|
||||
/* returns a segment id */
|
||||
/************************************************************************/
|
||||
static IpcMemoryId
|
||||
SISegmentGet(int key, /* the corresponding key for the segment */
|
||||
int size, /* size of segment in bytes */
|
||||
bool create)
|
||||
{
|
||||
IpcMemoryId shmid;
|
||||
|
||||
if (create) {
|
||||
shmid = IpcMemoryCreate(key, size, IPCProtection);
|
||||
} else {
|
||||
shmid = IpcMemoryIdGet(key, size);
|
||||
}
|
||||
return(shmid);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* SISegmentAttach(shmid) - attach a shared segment with id shmid */
|
||||
/************************************************************************/
|
||||
static void
|
||||
SISegmentAttach(IpcMemoryId shmid)
|
||||
{
|
||||
shmInvalBuffer = (struct SISeg *) IpcMemoryAttach(shmid);
|
||||
if (shmInvalBuffer == IpcMemAttachFailed) {
|
||||
/* XXX use validity function */
|
||||
elog(NOTICE, "SISegmentAttach: Could not attach segment");
|
||||
elog(FATAL, "SISegmentAttach: %m");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* SISegmentInit(killExistingSegment, key) initialize segment */
|
||||
/************************************************************************/
|
||||
int
|
||||
SISegmentInit(bool killExistingSegment, IPCKey key)
|
||||
{
|
||||
SISegOffsets *oP;
|
||||
int segSize;
|
||||
IpcMemoryId shmId;
|
||||
bool create;
|
||||
|
||||
if (killExistingSegment) {
|
||||
/* Kill existing segment */
|
||||
/* set semaphore */
|
||||
SISegmentKill(key);
|
||||
|
||||
/* Get a shared segment */
|
||||
|
||||
oP = SIComputeSize(&segSize);
|
||||
/*
|
||||
* Be tidy
|
||||
*/
|
||||
pfree(oP);
|
||||
|
||||
create = true;
|
||||
shmId = SISegmentGet(key,segSize, create);
|
||||
if (shmId < 0) {
|
||||
perror("SISegmentGet: failed");
|
||||
return(-1); /* an error */
|
||||
}
|
||||
|
||||
/* Attach the shared cache invalidation segment */
|
||||
/* sets the global variable shmInvalBuffer */
|
||||
SISegmentAttach(shmId);
|
||||
|
||||
/* Init shared memory table */
|
||||
SISegInit(shmInvalBuffer);
|
||||
} else {
|
||||
/* use an existing segment */
|
||||
create = false;
|
||||
shmId = SISegmentGet(key, 0, create);
|
||||
if (shmId < 0) {
|
||||
perror("SISegmentGet: getting an existent segment failed");
|
||||
return(-1); /* an error */
|
||||
}
|
||||
/* Attach the shared cache invalidation segment */
|
||||
SISegmentAttach(shmId);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
247
src/backend/storage/ipc/spin.c
Normal file
247
src/backend/storage/ipc/spin.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* spin.c--
|
||||
* routines for managing spin locks
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* POSTGRES has two kinds of locks: semaphores (which put the
|
||||
* process to sleep) and spinlocks (which are supposed to be
|
||||
* short term locks). Currently both are implemented as SysV
|
||||
* semaphores, but presumably this can change if we move to
|
||||
* a machine with a test-and-set (TAS) instruction. Its probably
|
||||
* a good idea to think about (and allocate) short term and long
|
||||
* term semaphores separately anyway.
|
||||
*
|
||||
* NOTE: These routines are not supposed to be widely used in Postgres.
|
||||
* They are preserved solely for the purpose of porting Mark Sullivan's
|
||||
* buffer manager to Postgres.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include "postgres.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/spin.h"
|
||||
#include "storage/proc.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
/* globals used in this file */
|
||||
IpcSemaphoreId SpinLockId;
|
||||
|
||||
#ifdef HAS_TEST_AND_SET
|
||||
/* real spin lock implementations */
|
||||
|
||||
bool
|
||||
CreateSpinlocks(IPCKey key)
|
||||
{
|
||||
/* the spin lock shared memory must have been created by now */
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
bool
|
||||
AttachSpinLocks(IPCKey key)
|
||||
{
|
||||
/* the spin lock shared memory must have been attached by now */
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
bool
|
||||
InitSpinLocks(int init, IPCKey key)
|
||||
{
|
||||
extern SPINLOCK ShmemLock;
|
||||
extern SPINLOCK BindingLock;
|
||||
extern SPINLOCK BufMgrLock;
|
||||
extern SPINLOCK LockMgrLock;
|
||||
extern SPINLOCK ProcStructLock;
|
||||
extern SPINLOCK SInvalLock;
|
||||
extern SPINLOCK OidGenLockId;
|
||||
|
||||
#ifdef MAIN_MEMORY
|
||||
extern SPINLOCK MMCacheLock;
|
||||
#endif /* SONY_JUKEBOX */
|
||||
|
||||
/* These six spinlocks have fixed location is shmem */
|
||||
ShmemLock = (SPINLOCK) SHMEMLOCKID;
|
||||
BindingLock = (SPINLOCK) BINDINGLOCKID;
|
||||
BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
|
||||
LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
|
||||
ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
|
||||
SInvalLock = (SPINLOCK) SINVALLOCKID;
|
||||
OidGenLockId = (SPINLOCK) OIDGENLOCKID;
|
||||
|
||||
#ifdef MAIN_MEMORY
|
||||
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
|
||||
#endif /* MAIN_MEMORY */
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
SpinAcquire(SPINLOCK lock)
|
||||
{
|
||||
ExclusiveLock(lock);
|
||||
PROC_INCR_SLOCK(lock);
|
||||
}
|
||||
|
||||
void
|
||||
SpinRelease(SPINLOCK lock)
|
||||
{
|
||||
PROC_DECR_SLOCK(lock);
|
||||
ExclusiveUnlock(lock);
|
||||
}
|
||||
|
||||
bool
|
||||
SpinIsLocked(SPINLOCK lock)
|
||||
{
|
||||
return(!LockIsFree(lock));
|
||||
}
|
||||
|
||||
#else /* HAS_TEST_AND_SET */
|
||||
/* Spinlocks are implemented using SysV semaphores */
|
||||
|
||||
|
||||
/*
|
||||
* SpinAcquire -- try to grab a spinlock
|
||||
*
|
||||
* FAILS if the semaphore is corrupted.
|
||||
*/
|
||||
void
|
||||
SpinAcquire(SPINLOCK lock)
|
||||
{
|
||||
IpcSemaphoreLock(SpinLockId, lock, IpcExclusiveLock);
|
||||
PROC_INCR_SLOCK(lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* SpinRelease -- release a spin lock
|
||||
*
|
||||
* FAILS if the semaphore is corrupted
|
||||
*/
|
||||
void
|
||||
SpinRelease(SPINLOCK lock)
|
||||
{
|
||||
Assert(SpinIsLocked(lock))
|
||||
PROC_DECR_SLOCK(lock);
|
||||
IpcSemaphoreUnlock(SpinLockId, lock, IpcExclusiveLock);
|
||||
}
|
||||
|
||||
bool
|
||||
SpinIsLocked(SPINLOCK lock)
|
||||
{
|
||||
int semval;
|
||||
|
||||
semval = IpcSemaphoreGetValue(SpinLockId, lock);
|
||||
return(semval < IpcSemaphoreDefaultStartValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* CreateSpinlocks -- Create a sysV semaphore array for
|
||||
* the spinlocks
|
||||
*
|
||||
*/
|
||||
bool
|
||||
CreateSpinlocks(IPCKey key)
|
||||
{
|
||||
|
||||
int status;
|
||||
IpcSemaphoreId semid;
|
||||
semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection,
|
||||
IpcSemaphoreDefaultStartValue, 1, &status);
|
||||
if (status == IpcSemIdExist) {
|
||||
IpcSemaphoreKill(key);
|
||||
elog(NOTICE,"Destroying old spinlock semaphore");
|
||||
semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection,
|
||||
IpcSemaphoreDefaultStartValue, 1, &status);
|
||||
}
|
||||
|
||||
if (semid >= 0) {
|
||||
SpinLockId = semid;
|
||||
return(TRUE);
|
||||
}
|
||||
/* cannot create spinlocks */
|
||||
elog(FATAL,"CreateSpinlocks: cannot create spin locks");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach to existing spinlock set
|
||||
*/
|
||||
bool
|
||||
AttachSpinLocks(IPCKey key)
|
||||
{
|
||||
IpcSemaphoreId id;
|
||||
|
||||
id = semget (key, MAX_SPINS, 0);
|
||||
if (id < 0) {
|
||||
if (errno == EEXIST) {
|
||||
/* key is the name of someone else's semaphore */
|
||||
elog (FATAL,"AttachSpinlocks: SPIN_KEY belongs to someone else");
|
||||
}
|
||||
/* cannot create spinlocks */
|
||||
elog(FATAL,"AttachSpinlocks: cannot create spin locks");
|
||||
return(FALSE);
|
||||
}
|
||||
SpinLockId = id;
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* InitSpinLocks -- Spinlock bootstrapping
|
||||
*
|
||||
* We need several spinlocks for bootstrapping:
|
||||
* BindingLock (for the shmem binding table) and
|
||||
* ShmemLock (for the shmem allocator), BufMgrLock (for buffer
|
||||
* pool exclusive access), LockMgrLock (for the lock table), and
|
||||
* ProcStructLock (a spin lock for the shared process structure).
|
||||
* If there's a Sony WORM drive attached, we also have a spinlock
|
||||
* (SJCacheLock) for it. Same story for the main memory storage mgr.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
InitSpinLocks(int init, IPCKey key)
|
||||
{
|
||||
extern SPINLOCK ShmemLock;
|
||||
extern SPINLOCK BindingLock;
|
||||
extern SPINLOCK BufMgrLock;
|
||||
extern SPINLOCK LockMgrLock;
|
||||
extern SPINLOCK ProcStructLock;
|
||||
extern SPINLOCK SInvalLock;
|
||||
extern SPINLOCK OidGenLockId;
|
||||
|
||||
#ifdef MAIN_MEMORY
|
||||
extern SPINLOCK MMCacheLock;
|
||||
#endif /* MAIN_MEMORY */
|
||||
|
||||
if (!init || key != IPC_PRIVATE) {
|
||||
/* if bootstrap and key is IPC_PRIVATE, it means that we are running
|
||||
* backend by itself. no need to attach spinlocks
|
||||
*/
|
||||
if (! AttachSpinLocks(key)) {
|
||||
elog(FATAL,"InitSpinLocks: couldnt attach spin locks");
|
||||
return(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* These five (or six) spinlocks have fixed location is shmem */
|
||||
ShmemLock = (SPINLOCK) SHMEMLOCKID;
|
||||
BindingLock = (SPINLOCK) BINDINGLOCKID;
|
||||
BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
|
||||
LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
|
||||
ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
|
||||
SInvalLock = (SPINLOCK) SINVALLOCKID;
|
||||
OidGenLockId = (SPINLOCK) OIDGENLOCKID;
|
||||
|
||||
#ifdef MAIN_MEMORY
|
||||
MMCacheLock = (SPINLOCK) MMCACHELOCKID;
|
||||
#endif /* MAIN_MEMORY */
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
#endif /* HAS_TEST_AND_SET */
|
||||
Reference in New Issue
Block a user