/* ----------------------------------------------------------------
 *   FILE
 * 	sinvaladt.c
 *	
 *   DESCRIPTION
 *	POSTGRES shared cache invalidation segment definitions.
 *
 *   INTERFACE ROUTINES
 *
 *   NOTES
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/storage/ipc/RCS/sinvaladt.c,v 1.26 1992/07/30 19:51:20 mer Exp $
 * ----------------------------------------------------------------
 */

/* ----------------
 *	include files
 *
 *	XXX yucky order dependency
 * ----------------
 */

#include "storage/ipci.h"
#include "storage/ipc.h"
#include "storage/sinvaladt.h"
#include "storage/lmgr.h"
#include "utils/log.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;

OID 		SIDbId = 0;	    
LRelId  	SIRelId;    	    
OID 		SIDummyOid = InvalidObjectId;

#ifdef _FOO_BAR_BAZ_
/*
 * XXX this won't work now with 4 byte xid's it should probably be torched.
 */
TransactionIdData	SIXidData;
TransactionId		SIXid = &SIXidData;
#endif

/* ----------------
 *	declarations
 * ----------------
 */

/*
 * SISetActiveProcess --
 */
extern
BackendId
SIAssignBackendId ARGS((
	SISeg 		*segInOutP,
	BackendTag	tag
));

/*
 * CleanupInvalidationState --
 * Note:
 *	This is a temporary hack.  ExitBackend should call this instead
 *	of exit (via on_exitpg).
 */
extern
void
CleanupInvalidationState ARGS((
	int		status,	/* XXX */
	SISeg		*segInOutP
));

/************************************************************************/
/* 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 */
void
SISetActiveProcess(segInOutP, backendId)
    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(segInOutP)
    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, segInOutP);
    return 1;
}

/* ----------------
 *	SIAssignBackendId
 * ----------------
 */
BackendId
SIAssignBackendId(segInOutP, backendTag)
    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 	*/
/************************************************************************/
void
SISetDeadProcess(segP, backendId)
    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
 * ----------------
 */
void
CleanupInvalidationState(status, segInOutP)
    int		status;		/* XXX */
    SISeg	*segInOutP;	/* XXX style */
{
    Assert(PointerIsValid(segInOutP));

    SISetDeadProcess(segInOutP, MyBackendId);
}


/************************************************************************/
/* SIComputeSize()  - retuns the size of a buffer segment   	    	*/
/************************************************************************/
SISegOffsets *
SIComputeSize(segSize)
    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);
}


/************************************************************************/
/* SIGetSemaphoreId - returns the general semaphore Id	    	    	*/
/************************************************************************/
SIGetSemaphoreId(segP)
    SISeg *segP;
{
    return(segP->generalSemaphoreId);
}


/************************************************************************/
/* SISetSemaphoreId 	- sets the general semaphore Id	    	    	*/
/************************************************************************/
void
SISetSemaphoreId(segP, id)
    SISeg *segP;
    IpcSemaphoreId id;
{
    segP->generalSemaphoreId = id;
}


/************************************************************************/
/* SISetStartEntrySection(segP, offset)     - sets the offset		*/
/************************************************************************/
void
SISetStartEntrySection(segP, offset)
    SISeg *segP;
    Offset offset;
{
    segP->startEntrySection = offset;
}

/************************************************************************/
/* SIGetStartEntrySection(segP)     - returnss the offset   		*/
/************************************************************************/
Offset
SIGetStartEntrySection(segP)
SISeg *segP;
{
    return(segP->startEntrySection);
}


/************************************************************************/
/* SISetEndEntrySection(segP, offset) 	- sets the offset   		*/
/************************************************************************/
void
SISetEndEntrySection(segP, offset)
    SISeg *segP;
    Offset offset;
{
    segP->endEntrySection = offset;
}

/************************************************************************/
/* SIGetEndEntrySection(segP) 	- returnss the offset	    	    	*/
/************************************************************************/
Offset
SIGetEndEntrySection(segP)
    SISeg *segP;
{
    return(segP->endEntrySection);
}

/************************************************************************/
/* SISetEndEntryChain(segP, offset) 	- sets the offset   	    	*/
/************************************************************************/
void
SISetEndEntryChain(segP, offset)
    SISeg *segP;
    Offset offset;
{
    segP->endEntryChain = offset;
}

/************************************************************************/
/* SIGetEndEntryChain(segP) 	- returnss the offset	    	    	*/
/************************************************************************/
Offset
SIGetEndEntryChain(segP)
    SISeg *segP;
{
    return(segP->endEntryChain);
}

/************************************************************************/
/* SISetStartEntryChain(segP, offset) 	- sets the offset   	    	*/
/************************************************************************/
void
SISetStartEntryChain(segP, offset)
    SISeg *segP;
    Offset offset;
{
    segP->startEntryChain = offset;
}

/************************************************************************/
/* SIGetStartEntryChain(segP) 	- returns  the offset	    	    	*/
/************************************************************************/
Offset
SIGetStartEntryChain(segP)
    SISeg *segP;
{
    return(segP->startEntryChain);
}

/************************************************************************/
/* SISetNumEntries(segP, num)	sets the current nuber of entries   	*/
/************************************************************************/
bool
SISetNumEntries(segP, num)
    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  	*/
/************************************************************************/
int
SIGetNumEntries(segP)
    SISeg *segP;
{
    return(segP->numEntries);
}


/************************************************************************/
/* SISetMaxNumEntries(segP, num)    sets the maximal number of entries	*/
/************************************************************************/
bool
SISetMaxNumEntries(segP, num)
    SISeg *segP;
    int   num;
{
    if ( num <= MAXNUMMESSAGES) {
        segP->maxNumEntries =  num;
        return(true);
    } else {
        return(false);  /* wrong number */
    }   
}


/************************************************************************/
/* SIGetMaxNumEntries(segP) 	returns the maximal number of entries	*/
/************************************************************************/
int
SIGetMaxNumEntries(segP)
    SISeg *segP;
{
    return(segP->maxNumEntries);
}

/************************************************************************/
/* SIGetProcStateLimit(segP, i)	returns the limit of read messages  	*/
/************************************************************************/
int
SIGetProcStateLimit(segP, i) 
    SISeg 	*segP;
    int 	i;
{
    return(segP->procState[i].limit);
}

/************************************************************************/
/* SIGetProcStateResetState(segP, i)  returns the limit of read messages*/
/************************************************************************/
bool
SIGetProcStateResetState(segP, i) 
    SISeg 	*segP;
    int 	i;
{
    return((bool) segP->procState[i].resetState);
}

/************************************************************************/
/* SIIncNumEntries(segP, num)	increments the current nuber of entries	*/
/************************************************************************/
bool
SIIncNumEntries(segP, num)
    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	*/
/************************************************************************/
bool
SIDecNumEntries(segP, num)
    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	    	    	*/
/************************************************************************/
void
SISetStartFreeSpace(segP, offset)
    SISeg *segP;
    Offset offset;
{
    segP->startFreeSpace = offset;
}

/************************************************************************/
/* SIGetStartFreeSpace(segP)  - returns the offset  	    	    	*/
/************************************************************************/
Offset
SIGetStartFreeSpace(segP)
    SISeg *segP;
{
    return(segP->startFreeSpace);
}



/************************************************************************/
/* SIGetFirstDataEntry(segP)  returns first data entry	    	    	*/
/************************************************************************/
SISegEntry *
SIGetFirstDataEntry(segP)
    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   	*/
/************************************************************************/
SISegEntry *
SIGetLastDataEntry(segP)
    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	    	*/
/************************************************************************/
SISegEntry *
SIGetNextDataEntry(segP, offset)
    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	*/
/************************************************************************/
SISegEntry *
SIGetNthDataEntry(segP, n)
    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  	*/
/************************************************************************/
Offset
SIEntryOffset(segP, entryP)
    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(segP, data)
    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 	    	*/
/************************************************************************/
void
SIDecProcLimit(segP, num)
    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(segP)
    SISeg 	*segP;
{
    SISegEntry 	    *eP, *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(segP)
    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(segP, backendId, invalFunction, resetFunction)
    SISeg   *segP;
    int     backendId;
    void    (*invalFunction)();
    void    (*resetFunction)();
{
    int i;
    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 */
    	i = 0;
    	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(segP)
    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	    	    	    	*/
/************************************************************************/
void
SISegInit(segP)
    SISeg *segP;
{
    SISegOffsets    *oP;
    SISegOffsets    *SIComputeSize();
    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                              */
/************************************************************************/
void
SISegmentKill(key)
    int key;                    /* the corresponding key for the segment    */
{   
    IpcMemoryKill(key);
}	


/************************************************************************/
/* SISegmentGet(key, size)  - get a shared segment of size <size>       */
/*                returns a segment id                                  */
/************************************************************************/
IpcMemoryId
SISegmentGet(key, size, create)
    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     */
/************************************************************************/
void
SISegmentAttach(shmid)
    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(killExistingSegment, key)
    bool    killExistingSegment;
    IPCKey  key;
{ 
    SISegOffsets	*oP;
    int     	    	status, 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);
}


/* synchronization of the shared buffer access	    	    */

void
SISyncInit(key)
    IPCKey	key;
{
#ifdef HAS_TEST_AND_SET
    SharedInvalidationLockId = (int)SINVALLOCKID;  /* a fixed lock */
#else /* HAS_TEST_AND_SET */
    int status;
    SharedInvalidationSemaphore =
    	IpcSemaphoreCreate(key, 1, IPCProtection, SI_LockStartValue,
    	                   &status);
    if(0) { /* XXX use validity function and check status */
    	elog(FATAL, "SISynchInit: %m");
    }
#endif /* HAS_TEST_AND_SET */
}

void
SISyncKill(key)
    IPCKey	key;
{
#ifndef HAS_TEST_AND_SET
    IpcSemaphoreKill(key);
#endif
}


void
SIReadLock()
{
#ifdef HAS_TEST_AND_SET
    SharedLock(SharedInvalidationLockId);
#else /* HAS_TEST_AND_SET */
    IpcSemaphoreLock ( SharedInvalidationSemaphore ,
		       0 , 
		       SI_SharedLock );
#endif /* HAS_TEST_AND_SET */
}


void
SIWriteLock()
{
#ifdef HAS_TEST_AND_SET
    ExclusiveLock(SharedInvalidationLockId);
#else /* HAS_TEST_AND_SET */
    IpcSemaphoreLock ( SharedInvalidationSemaphore,
		       0 ,
		      SI_ExclusiveLock );
#endif /* HAS_TEST_AND_SET */
}


void
SIReadUnlock()
{
#ifdef HAS_TEST_AND_SET
    SharedUnlock(SharedInvalidationLockId);
#else /* HAS_TEST_AND_SET */
    IpcSemaphoreLock ( SharedInvalidationSemaphore,
		       0,
		       (- SI_SharedLock) );
#endif /* HAS_TEST_AND_SET */
}


void
SIWriteUnlock()
{
#ifdef HAS_TEST_AND_SET
    ExclusiveUnlock(SharedInvalidationLockId);
#else /* HAS_TEST_AND_SET */
    IpcSemaphoreLock ( SharedInvalidationSemaphore,
		       0,
		       ( - SI_ExclusiveLock ) );
#endif /* HAS_TEST_AND_SET */
}



#ifdef _FOO_BAR_BAZ_
/************************************************************************
* debug routines
*
* This code tries to use the old lmgr setup and should not be called 
* unless you are willing to set up a spin lock for it as was done above
* to handle shared invaladation processing.
*************************************************************************/
void
SIBufferImage()
{
    int i, status;
    SISegEntry 	*SIGetFirstDataEntry();
    SISegEntry 	*eP;
    SISeg   	*segP;
    
    /* READ LOCK buffer */
    status = LMLock(SILockTableId, LockWait, &SIRelId, &SIDummyOid, &SIDummyOid,
    	    	    SIXid, MultiLevelLockRequest_ReadRelation);
    if (status == L_ERROR) {
    	    elog(FATAL, "InvalidateSharedInvalid: Could not lock buffer segment");
    }
    
    segP = shmInvalBuffer;
    fprintf(stderr, "\nDEBUG: current segment IMAGE");
    fprintf(stderr, "\n startEntrySection: %d", SIGetStartEntrySection(segP));
    fprintf(stderr, "\n endEntrySection: %d", SIGetEndEntrySection(segP));
    fprintf(stderr, "\n startFreeSpace: %d", SIGetStartFreeSpace(segP));
    fprintf(stderr, "\n startEntryChain: %d", SIGetStartEntryChain(segP));
    fprintf(stderr, "\n endEntryChain: %d", SIGetEndEntryChain(segP));
    fprintf(stderr, "\n numEntries: %d", SIGetNumEntries(segP));
    fprintf(stderr, "\n maxNumEntries: %d", SIGetMaxNumEntries(segP));
    for (i = 0; i < MaxBackendId; i++) {
    	fprintf(stderr, "\n   Backend[%2d] - limit: %3d  reset: %s", 
    	    	    i + 1,
    	    	    SIGetProcStateLimit(segP, i),
    	    	    SIGetProcStateResetState(segP, i) ? "true" : "false");
    }
    fprintf(stderr, "\n Message entries:");
    eP = SIGetFirstDataEntry(segP);
    while (eP != NULL) {
    	fprintf(stderr, "\n  cacheId: %-7d", eP->entryData.cacheId);
    	fprintf(stderr, "   free: %s", eP->isfree ? "true" : "false");
    	fprintf(stderr, "   my offset: %-4d", SIEntryOffset(segP, eP));
    	fprintf(stderr, "   next: %-4d", eP->next);
    	eP = SIGetNextDataEntry(segP, eP->next);
    }
    fprintf(stderr, "\n No further entries");
    
    fprintf(stderr, "\n No. of free entries: %d", 
    	    	    (SIGetMaxNumEntries(segP) - SIGetNumEntries(segP)));
/*  fprintf(stderr, "\n Free space chain:");
    eP = SIGetNextDataEntry(segP, SIGetStartFreeSpace(segP));
    while (eP != NULL) {
    	fprintf(stderr, "\n  free: %s", eP->isfree ? "true" : "false");
    	fprintf(stderr, "   my offset: %-4d", SIEntryOffset(segP, eP));
    	fprintf(stderr, "   next: %-4d", eP->next);
    	eP = SIGetNextDataEntry(segP, eP->next);
    }
    fprintf(stderr, "\n No further entries");
*/  
    fprintf(stderr, "\n");
    
    /* UNLOCK buffer */
    status = LMLockReleaseAll(SILockTableId, SIXid);
    if (status == L_ERROR) {
    	    elog(FATAL, "InvalidateSharedInvalid: Could not unlock buffer segment");
    }
}
#endif _FOO_BAR_BAZ_

/****************************************************************************/
/*  Invalidation functions for testing cache invalidation   	    	    */
/****************************************************************************/
void
SIinvalFunc(data)
    int data;
{   
    printf(" Backend: %d -- invalidating data (cacheId) %d\n",
    	    MyBackendId, data);
}

void
SIresetFunc()
{   
    printf("\n Backend: %d -- state reset done",
    	    MyBackendId);
}
