/* * ReceiveSharedInvalidMessages * Process shared-cache-invalidation messages waiting for this backend * * We guarantee to process all messages that had been queued before the * routine was entered. It is of course possible for more messages to get * queued right after our last SIGetDataEntries call. * * NOTE: it is entirely possible for this routine to be invoked recursively * as a consequence of processing inside the invalFunction or resetFunction. * Furthermore, such a recursive call must guarantee that all outstanding * inval messages have been processed before it exits. This is the reason * for the strange-looking choice to use a statically allocated buffer array * and counters; it's so that a recursive call can process messages already * sucked out of sinvaladt.c. */ void ReceiveSharedInvalidMessages( void (*invalFunction) (SharedInvalidationMessage *msg), void (*resetFunction) (void)) { #define MAXINVALMSGS 32 static SharedInvalidationMessage messages[MAXINVALMSGS]; /* * We use volatile here to prevent bugs if a compiler doesn't realize that * recursion is a possibility ... */ static volatile int nextmsg = 0; static volatile int nummsgs = 0; /* Deal with any messages still pending from an outer recursion */ while (nextmsg < nummsgs) { SharedInvalidationMessage *msg = &messages[nextmsg++]; invalFunction(msg); } do { int getResult; nextmsg = nummsgs = 0; /* Try to get some more messages */ getResult = SIGetDataEntries(messages, MAXINVALMSGS); if (getResult < 0) { /* got a reset message */ elog(DEBUG4, "cache state reset"); resetFunction(); break; /* nothing more to do */ } /* Process them, being wary that a recursive call might eat some */ nextmsg = 0; nummsgs = getResult; while (nextmsg < nummsgs) { SharedInvalidationMessage *msg = &messages[nextmsg++]; invalFunction(msg); } /* * We only need to loop if the last SIGetDataEntries call (which might * have been within a recursive call) returned a full buffer. */ } while (nummsgs == MAXINVALMSGS); /* * We are now caught up. If we received a catchup signal, reset that * flag, and call SICleanupQueue(). This is not so much because we need * to flush dead messages right now, as that we want to pass on the * catchup signal to the next slowest backend. "Daisy chaining" the * catchup signal this way avoids creating spikes in system load for what * should be just a background maintenance activity. */ if (catchupInterruptOccurred) { catchupInterruptOccurred = 0; elog(DEBUG4, "sinval catchup complete, cleaning queue"); SICleanupQueue(false, 0); } }
/* * SIInsertDataEntries * Add new invalidation message(s) to the buffer. */ void SIInsertDataEntries(const SharedInvalidationMessage *data, int n) { SISeg *segP = shmInvalBuffer; /* * N can be arbitrarily large. We divide the work into groups of no more * than WRITE_QUANTUM messages, to be sure that we don't hold the lock for * an unreasonably long time. (This is not so much because we care about * letting in other writers, as that some just-caught-up backend might be * trying to do SICleanupQueue to pass on its signal, and we don't want it * to have to wait a long time.) Also, we need to consider calling * SICleanupQueue every so often. */ while (n > 0) { int nthistime = Min(n, WRITE_QUANTUM); int numMsgs; int max; n -= nthistime; LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); /* * If the buffer is full, we *must* acquire some space. Clean the * queue and reset anyone who is preventing space from being freed. * Otherwise, clean the queue only when it's exceeded the next * fullness threshold. We have to loop and recheck the buffer state * after any call of SICleanupQueue. */ for (;;) { numMsgs = segP->maxMsgNum - segP->minMsgNum; if (numMsgs + nthistime > MAXNUMMESSAGES || numMsgs >= segP->nextThreshold) SICleanupQueue(true, nthistime); else break; } /* * Insert new message(s) into proper slot of circular buffer */ max = segP->maxMsgNum; while (nthistime-- > 0) { segP->buffer[max % MAXNUMMESSAGES] = *data++; max++; } /* Update current value of maxMsgNum using spinlock */ { /* use volatile pointer to prevent code rearrangement */ volatile SISeg *vsegP = segP; SpinLockAcquire(&vsegP->msgnumLock); vsegP->maxMsgNum = max; SpinLockRelease(&vsegP->msgnumLock); } LWLockRelease(SInvalWriteLock); } }
/* * SIInsertDataEntries * Add new invalidation message(s) to the buffer. */ void SIInsertDataEntries(const SharedInvalidationMessage *data, int n) { SISeg *segP = shmInvalBuffer; /* * N can be arbitrarily large. We divide the work into groups of no more * than WRITE_QUANTUM messages, to be sure that we don't hold the lock for * an unreasonably long time. (This is not so much because we care about * letting in other writers, as that some just-caught-up backend might be * trying to do SICleanupQueue to pass on its signal, and we don't want it * to have to wait a long time.) Also, we need to consider calling * SICleanupQueue every so often. */ while (n > 0) { int nthistime = Min(n, WRITE_QUANTUM); int numMsgs; int max; int i; n -= nthistime; LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); /* * If the buffer is full, we *must* acquire some space. Clean the * queue and reset anyone who is preventing space from being freed. * Otherwise, clean the queue only when it's exceeded the next * fullness threshold. We have to loop and recheck the buffer state * after any call of SICleanupQueue. */ for (;;) { numMsgs = segP->maxMsgNum - segP->minMsgNum; if (numMsgs + nthistime > MAXNUMMESSAGES || numMsgs >= segP->nextThreshold) SICleanupQueue(true, nthistime); else break; } /* * Insert new message(s) into proper slot of circular buffer */ max = segP->maxMsgNum; while (nthistime-- > 0) { segP->buffer[max % MAXNUMMESSAGES] = *data++; max++; } /* Update current value of maxMsgNum using spinlock */ SpinLockAcquire(&segP->msgnumLock); segP->maxMsgNum = max; SpinLockRelease(&segP->msgnumLock); /* * Now that the maxMsgNum change is globally visible, we give everyone * a swift kick to make sure they read the newly added messages. * Releasing SInvalWriteLock will enforce a full memory barrier, so * these (unlocked) changes will be committed to memory before we exit * the function. */ for (i = 0; i < segP->lastBackend; i++) { ProcState *stateP = &segP->procState[i]; stateP->hasMessages = true; } LWLockRelease(SInvalWriteLock); } }