1
0
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:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View 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

View 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.

View 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 */

View 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);
}

View 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*/

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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 */