Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions | Variables

sinval.c File Reference

#include "postgres.h"
#include "access/xact.h"
#include "commands/async.h"
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/sinvaladt.h"
#include "utils/inval.h"
Include dependency graph for sinval.c:

Go to the source code of this file.

Defines

#define MAXINVALMSGS   32

Functions

static void ProcessCatchupEvent (void)
void SendSharedInvalidMessages (const SharedInvalidationMessage *msgs, int n)
void ReceiveSharedInvalidMessages (void(*invalFunction)(SharedInvalidationMessage *msg), void(*resetFunction)(void))
void HandleCatchupInterrupt (void)
void EnableCatchupInterrupt (void)
bool DisableCatchupInterrupt (void)

Variables

uint64 SharedInvalidMessageCounter
static volatile int catchupInterruptEnabled = 0
static volatile int catchupInterruptOccurred = 0

Define Documentation

#define MAXINVALMSGS   32

Function Documentation

bool DisableCatchupInterrupt ( void   ) 
void EnableCatchupInterrupt ( void   ) 

Definition at line 237 of file sinval.c.

References catchupInterruptEnabled, catchupInterruptOccurred, and ProcessCatchupEvent().

Referenced by AutoVacLauncherMain(), prepare_for_client_read(), and ProcessIncomingNotify().

{
    /*
     * This code is tricky because we are communicating with a signal handler
     * that could interrupt us at any point.  If we just checked
     * catchupInterruptOccurred and then set catchupInterruptEnabled, we could
     * fail to respond promptly to a signal that happens in between those two
     * steps.  (A very small time window, perhaps, but Murphy's Law says you
     * can hit it...)  Instead, we first set the enable flag, then test the
     * occurred flag.  If we see an unserviced interrupt has occurred, we
     * re-clear the enable flag before going off to do the service work. (That
     * prevents re-entrant invocation of ProcessCatchupEvent() if another
     * interrupt occurs.) If an interrupt comes in between the setting and
     * clearing of catchupInterruptEnabled, then it will have done the service
     * work and left catchupInterruptOccurred zero, so we have to check again
     * after clearing enable.  The whole thing has to be in a loop in case
     * another interrupt occurs while we're servicing the first. Once we get
     * out of the loop, enable is set and we know there is no unserviced
     * interrupt.
     *
     * NB: an overenthusiastic optimizing compiler could easily break this
     * code. Hopefully, they all understand what "volatile" means these days.
     */
    for (;;)
    {
        catchupInterruptEnabled = 1;
        if (!catchupInterruptOccurred)
            break;
        catchupInterruptEnabled = 0;
        if (catchupInterruptOccurred)
            ProcessCatchupEvent();
    }
}

void HandleCatchupInterrupt ( void   ) 

Definition at line 166 of file sinval.c.

References catchupInterruptEnabled, catchupInterruptOccurred, CHECK_FOR_INTERRUPTS, ImmediateInterruptOK, proc_exit_inprogress, and ProcessCatchupEvent().

Referenced by procsignal_sigusr1_handler().

{
    /*
     * Note: this is called by a SIGNAL HANDLER. You must be very wary what
     * you do here.
     */

    /* Don't joggle the elbow of proc_exit */
    if (proc_exit_inprogress)
        return;

    if (catchupInterruptEnabled)
    {
        bool        save_ImmediateInterruptOK = ImmediateInterruptOK;

        /*
         * We may be called while ImmediateInterruptOK is true; turn it off
         * while messing with the catchup state.  (We would have to save and
         * restore it anyway, because PGSemaphore operations inside
         * ProcessCatchupEvent() might reset it.)
         */
        ImmediateInterruptOK = false;

        /*
         * I'm not sure whether some flavors of Unix might allow another
         * SIGUSR1 occurrence to recursively interrupt this routine. To cope
         * with the possibility, we do the same sort of dance that
         * EnableCatchupInterrupt must do --- see that routine for comments.
         */
        catchupInterruptEnabled = 0;    /* disable any recursive signal */
        catchupInterruptOccurred = 1;   /* do at least one iteration */
        for (;;)
        {
            catchupInterruptEnabled = 1;
            if (!catchupInterruptOccurred)
                break;
            catchupInterruptEnabled = 0;
            if (catchupInterruptOccurred)
            {
                /* Here, it is finally safe to do stuff. */
                ProcessCatchupEvent();
            }
        }

        /*
         * Restore ImmediateInterruptOK, and check for interrupts if needed.
         */
        ImmediateInterruptOK = save_ImmediateInterruptOK;
        if (save_ImmediateInterruptOK)
            CHECK_FOR_INTERRUPTS();
    }
    else
    {
        /*
         * In this path it is NOT SAFE to do much of anything, except this:
         */
        catchupInterruptOccurred = 1;
    }
}

static void ProcessCatchupEvent ( void   )  [static]

Definition at line 303 of file sinval.c.

References AcceptInvalidationMessages(), CommitTransactionCommand(), DEBUG4, DisableNotifyInterrupt(), elog, EnableNotifyInterrupt(), IsTransactionOrTransactionBlock(), and StartTransactionCommand().

Referenced by EnableCatchupInterrupt(), and HandleCatchupInterrupt().

{
    bool        notify_enabled;

    /* Must prevent notify interrupt while I am running */
    notify_enabled = DisableNotifyInterrupt();

    /*
     * What we need to do here is cause ReceiveSharedInvalidMessages() to run,
     * which will do the necessary work and also reset the
     * catchupInterruptOccurred flag.  If we are inside a transaction we can
     * just call AcceptInvalidationMessages() to do this.  If we aren't, we
     * start and immediately end a transaction; the call to
     * AcceptInvalidationMessages() happens down inside transaction start.
     *
     * It is awfully tempting to just call AcceptInvalidationMessages()
     * without the rest of the xact start/stop overhead, and I think that
     * would actually work in the normal case; but I am not sure that things
     * would clean up nicely if we got an error partway through.
     */
    if (IsTransactionOrTransactionBlock())
    {
        elog(DEBUG4, "ProcessCatchupEvent inside transaction");
        AcceptInvalidationMessages();
    }
    else
    {
        elog(DEBUG4, "ProcessCatchupEvent outside transaction");
        StartTransactionCommand();
        CommitTransactionCommand();
    }

    if (notify_enabled)
        EnableNotifyInterrupt();
}

void ReceiveSharedInvalidMessages ( void(*)(SharedInvalidationMessage *msg)  invalFunction,
void(*)(void)  resetFunction 
)

Definition at line 77 of file sinval.c.

References catchupInterruptOccurred, DEBUG4, elog, MAXINVALMSGS, SharedInvalidMessageCounter, SICleanupQueue(), and SIGetDataEntries().

Referenced by AcceptInvalidationMessages().

{
#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++];

        SharedInvalidMessageCounter++;
        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");
            SharedInvalidMessageCounter++;
            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++];

            SharedInvalidMessageCounter++;
            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);
    }
}

void SendSharedInvalidMessages ( const SharedInvalidationMessage msgs,
int  n 
)

Variable Documentation

volatile int catchupInterruptEnabled = 0 [static]
volatile int catchupInterruptOccurred = 0 [static]