Header And Logo

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

Data Structures | Defines | Typedefs | Functions | Variables

timeout.c File Reference

#include "postgres.h"
#include <sys/time.h>
#include "storage/proc.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
Include dependency graph for timeout.c:

Go to the source code of this file.

Data Structures

struct  timeout_params

Defines

#define disable_alarm()   (alarm_enabled = false)
#define enable_alarm()   (alarm_enabled = true)

Typedefs

typedef struct timeout_params timeout_params

Functions

static int find_active_timeout (TimeoutId id)
static void insert_timeout (TimeoutId id, int index)
static void remove_timeout_index (int index)
static void enable_timeout (TimeoutId id, TimestampTz now, TimestampTz fin_time)
static void schedule_alarm (TimestampTz now)
static void handle_sig_alarm (SIGNAL_ARGS)
void InitializeTimeouts (void)
TimeoutId RegisterTimeout (TimeoutId id, timeout_handler_proc handler)
void enable_timeout_after (TimeoutId id, int delay_ms)
void enable_timeout_at (TimeoutId id, TimestampTz fin_time)
void enable_timeouts (const EnableTimeoutParams *timeouts, int count)
void disable_timeout (TimeoutId id, bool keep_indicator)
void disable_timeouts (const DisableTimeoutParams *timeouts, int count)
void disable_all_timeouts (bool keep_indicators)
bool get_timeout_indicator (TimeoutId id, bool reset_indicator)
TimestampTz get_timeout_start_time (TimeoutId id)

Variables

static timeout_params all_timeouts [MAX_TIMEOUTS]
static bool all_timeouts_initialized = false
static volatile int num_active_timeouts = 0
static timeout_params *volatile active_timeouts [MAX_TIMEOUTS]
static volatile sig_atomic_t alarm_enabled = false

Define Documentation

#define disable_alarm (  )     (alarm_enabled = false)
#define enable_alarm (  )     (alarm_enabled = true)

Definition at line 64 of file timeout.c.

Referenced by schedule_alarm().


Typedef Documentation


Function Documentation

void disable_all_timeouts ( bool  keep_indicators  ) 

Definition at line 564 of file timeout.c.

References disable_alarm, elog, FATAL, i, ITIMER_REAL, MemSet, NULL, num_active_timeouts, and setitimer().

Referenced by AutoVacLauncherMain(), PostgresMain(), and ResolveRecoveryConflictWithBufferPin().

{
    disable_alarm();

    /*
     * Only bother to reset the timer if we think it's active.  We could just
     * let the interrupt happen anyway, but it's probably a bit cheaper to do
     * setitimer() than to let the useless interrupt happen.
     */
    if (num_active_timeouts > 0)
    {
        struct itimerval timeval;

        MemSet(&timeval, 0, sizeof(struct itimerval));
        if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
            elog(FATAL, "could not disable SIGALRM timer: %m");
    }

    num_active_timeouts = 0;

    if (!keep_indicators)
    {
        int         i;

        for (i = 0; i < MAX_TIMEOUTS; i++)
            all_timeouts[i].indicator = false;
    }
}

void disable_timeout ( TimeoutId  id,
bool  keep_indicator 
)

Definition at line 493 of file timeout.c.

References all_timeouts_initialized, Assert, disable_alarm, find_active_timeout(), GetCurrentTimestamp(), i, timeout_params::indicator, NULL, num_active_timeouts, remove_timeout_index(), and schedule_alarm().

Referenced by BackendInitialize(), finish_xact_command(), PerformAuthentication(), ProcSleep(), StandbyTimeoutHandler(), and start_xact_command().

{
    int         i;

    /* Assert request is sane */
    Assert(all_timeouts_initialized);
    Assert(all_timeouts[id].timeout_handler != NULL);

    /* Disable timeout interrupts for safety. */
    disable_alarm();

    /* Find the timeout and remove it from the active list. */
    i = find_active_timeout(id);
    if (i >= 0)
        remove_timeout_index(i);

    /* Mark it inactive, whether it was active or not. */
    if (!keep_indicator)
        all_timeouts[id].indicator = false;

    /* Reschedule the interrupt, if any timeouts remain active. */
    if (num_active_timeouts > 0)
        schedule_alarm(GetCurrentTimestamp());
}

void disable_timeouts ( const DisableTimeoutParams timeouts,
int  count 
)

Definition at line 529 of file timeout.c.

References all_timeouts_initialized, Assert, disable_alarm, find_active_timeout(), GetCurrentTimestamp(), i, DisableTimeoutParams::id, timeout_params::indicator, NULL, num_active_timeouts, remove_timeout_index(), and schedule_alarm().

Referenced by LockErrorCleanup(), and ProcSleep().

{
    int         i;

    Assert(all_timeouts_initialized);

    /* Disable timeout interrupts for safety. */
    disable_alarm();

    /* Cancel the timeout(s). */
    for (i = 0; i < count; i++)
    {
        TimeoutId   id = timeouts[i].id;
        int         idx;

        Assert(all_timeouts[id].timeout_handler != NULL);

        idx = find_active_timeout(id);
        if (idx >= 0)
            remove_timeout_index(idx);

        if (!timeouts[i].keep_indicator)
            all_timeouts[id].indicator = false;
    }

    /* Reschedule the interrupt, if any timeouts remain active. */
    if (num_active_timeouts > 0)
        schedule_alarm(GetCurrentTimestamp());
}

static void enable_timeout ( TimeoutId  id,
TimestampTz  now,
TimestampTz  fin_time 
) [static]

Definition at line 137 of file timeout.c.

References all_timeouts_initialized, Assert, timeout_params::fin_time, find_active_timeout(), i, timeout_params::indicator, insert_timeout(), NULL, num_active_timeouts, remove_timeout_index(), and timeout_params::start_time.

Referenced by enable_timeout_after(), enable_timeout_at(), and enable_timeouts().

{
    int         i;

    /* Assert request is sane */
    Assert(all_timeouts_initialized);
    Assert(all_timeouts[id].timeout_handler != NULL);

    /*
     * If this timeout was already active, momentarily disable it.  We
     * interpret the call as a directive to reschedule the timeout.
     */
    i = find_active_timeout(id);
    if (i >= 0)
        remove_timeout_index(i);

    /*
     * Find out the index where to insert the new timeout.  We sort by
     * fin_time, and for equal fin_time by priority.
     */
    for (i = 0; i < num_active_timeouts; i++)
    {
        timeout_params *old_timeout = active_timeouts[i];

        if (fin_time < old_timeout->fin_time)
            break;
        if (fin_time == old_timeout->fin_time && id < old_timeout->index)
            break;
    }

    /*
     * Mark the timeout active, and insert it into the active list.
     */
    all_timeouts[id].indicator = false;
    all_timeouts[id].start_time = now;
    all_timeouts[id].fin_time = fin_time;

    insert_timeout(id, i);
}

void enable_timeout_after ( TimeoutId  id,
int  delay_ms 
)

Definition at line 396 of file timeout.c.

References disable_alarm, enable_timeout(), GetCurrentTimestamp(), schedule_alarm(), and TimestampTzPlusMilliseconds.

Referenced by BackendInitialize(), PerformAuthentication(), ProcSleep(), ResolveRecoveryConflictWithBufferPin(), and start_xact_command().

{
    TimestampTz now;
    TimestampTz fin_time;

    /* Disable timeout interrupts for safety. */
    disable_alarm();

    /* Queue the timeout at the appropriate time. */
    now = GetCurrentTimestamp();
    fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
    enable_timeout(id, now, fin_time);

    /* Set the timer interrupt. */
    schedule_alarm(now);
}

void enable_timeout_at ( TimeoutId  id,
TimestampTz  fin_time 
)

Definition at line 421 of file timeout.c.

References disable_alarm, enable_timeout(), GetCurrentTimestamp(), and schedule_alarm().

{
    TimestampTz now;

    /* Disable timeout interrupts for safety. */
    disable_alarm();

    /* Queue the timeout at the appropriate time. */
    now = GetCurrentTimestamp();
    enable_timeout(id, now, fin_time);

    /* Set the timer interrupt. */
    schedule_alarm(now);
}

void enable_timeouts ( const EnableTimeoutParams timeouts,
int  count 
)

Definition at line 444 of file timeout.c.

References disable_alarm, elog, enable_timeout(), ERROR, GetCurrentTimestamp(), i, EnableTimeoutParams::id, schedule_alarm(), TimestampTzPlusMilliseconds, TMPARAM_AFTER, and TMPARAM_AT.

Referenced by ProcSleep(), and ResolveRecoveryConflictWithBufferPin().

{
    TimestampTz now;
    int         i;

    /* Disable timeout interrupts for safety. */
    disable_alarm();

    /* Queue the timeout(s) at the appropriate times. */
    now = GetCurrentTimestamp();

    for (i = 0; i < count; i++)
    {
        TimeoutId   id = timeouts[i].id;
        TimestampTz fin_time;

        switch (timeouts[i].type)
        {
            case TMPARAM_AFTER:
                fin_time = TimestampTzPlusMilliseconds(now,
                                                       timeouts[i].delay_ms);
                enable_timeout(id, now, fin_time);
                break;

            case TMPARAM_AT:
                enable_timeout(id, now, timeouts[i].fin_time);
                break;

            default:
                elog(ERROR, "unrecognized timeout type %d",
                     (int) timeouts[i].type);
                break;
        }
    }

    /* Set the timer interrupt. */
    schedule_alarm(now);
}

static int find_active_timeout ( TimeoutId  id  )  [static]

Definition at line 81 of file timeout.c.

References i, and num_active_timeouts.

Referenced by disable_timeout(), disable_timeouts(), and enable_timeout().

{
    int         i;

    for (i = 0; i < num_active_timeouts; i++)
    {
        if (active_timeouts[i]->index == id)
            return i;
    }

    return -1;
}

bool get_timeout_indicator ( TimeoutId  id,
bool  reset_indicator 
)

Definition at line 601 of file timeout.c.

References timeout_params::indicator.

Referenced by ProcessInterrupts().

{
    if (all_timeouts[id].indicator)
    {
        if (reset_indicator)
            all_timeouts[id].indicator = false;
        return true;
    }
    return false;
}

TimestampTz get_timeout_start_time ( TimeoutId  id  ) 

Definition at line 621 of file timeout.c.

References timeout_params::start_time.

Referenced by ProcSleep().

{
    return all_timeouts[id].start_time;
}

static void handle_sig_alarm ( SIGNAL_ARGS   )  [static]

Definition at line 258 of file timeout.c.

References alarm_enabled, disable_alarm, GetCurrentTimestamp(), timeout_params::indicator, MyProc, now(), num_active_timeouts, PGPROC::procLatch, remove_timeout_index(), schedule_alarm(), SetLatch(), and timeout_params::timeout_handler.

Referenced by InitializeTimeouts().

{
    int         save_errno = errno;

    /*
     * SIGALRM is always cause for waking anything waiting on the process
     * latch.  Cope with MyProc not being there, as the startup process also
     * uses this signal handler.
     */
    if (MyProc)
        SetLatch(&MyProc->procLatch);

    /*
     * Fire any pending timeouts, but only if we're enabled to do so.
     */
    if (alarm_enabled)
    {
        /*
         * Disable alarms, just in case this platform allows signal handlers
         * to interrupt themselves.  schedule_alarm() will re-enable if
         * appropriate.
         */
        disable_alarm();

        if (num_active_timeouts > 0)
        {
            TimestampTz now = GetCurrentTimestamp();

            /* While the first pending timeout has been reached ... */
            while (num_active_timeouts > 0 &&
                   now >= active_timeouts[0]->fin_time)
            {
                timeout_params *this_timeout = active_timeouts[0];

                /* Remove it from the active list */
                remove_timeout_index(0);

                /* Mark it as fired */
                this_timeout->indicator = true;

                /* And call its handler function */
                (*this_timeout->timeout_handler) ();

                /*
                 * The handler might not take negligible time (CheckDeadLock
                 * for instance isn't too cheap), so let's update our idea of
                 * "now" after each one.
                 */
                now = GetCurrentTimestamp();
            }

            /* Done firing timeouts, so reschedule next interrupt if any */
            schedule_alarm(now);
        }
    }

    errno = save_errno;
}

void InitializeTimeouts ( void   ) 
static void insert_timeout ( TimeoutId  id,
int  index 
) [static]

Definition at line 99 of file timeout.c.

References elog, FATAL, i, and num_active_timeouts.

Referenced by enable_timeout().

{
    int         i;

    if (index < 0 || index > num_active_timeouts)
        elog(FATAL, "timeout index %d out of range 0..%d", index,
             num_active_timeouts);

    for (i = num_active_timeouts - 1; i >= index; i--)
        active_timeouts[i + 1] = active_timeouts[i];

    active_timeouts[index] = &all_timeouts[id];

    num_active_timeouts++;
}

TimeoutId RegisterTimeout ( TimeoutId  id,
timeout_handler_proc  handler 
)

Definition at line 365 of file timeout.c.

References all_timeouts_initialized, Assert, ereport, errcode(), errmsg(), FATAL, NULL, timeout_params::timeout_handler, and USER_TIMEOUT.

Referenced by BackendInitialize(), InitPostgres(), and StartupProcessMain().

{
    Assert(all_timeouts_initialized);

    /* There's no need to disable the signal handler here. */

    if (id >= USER_TIMEOUT)
    {
        /* Allocate a user-defined timeout reason */
        for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
            if (all_timeouts[id].timeout_handler == NULL)
                break;
        if (id >= MAX_TIMEOUTS)
            ereport(FATAL,
                    (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
                     errmsg("cannot add more timeout reasons")));
    }

    Assert(all_timeouts[id].timeout_handler == NULL);

    all_timeouts[id].timeout_handler = handler;

    return id;
}

static void remove_timeout_index ( int  index  )  [static]

Definition at line 119 of file timeout.c.

References elog, FATAL, i, and num_active_timeouts.

Referenced by disable_timeout(), disable_timeouts(), enable_timeout(), and handle_sig_alarm().

{
    int         i;

    if (index < 0 || index >= num_active_timeouts)
        elog(FATAL, "timeout index %d out of range 0..%d", index,
             num_active_timeouts - 1);

    for (i = index + 1; i < num_active_timeouts; i++)
        active_timeouts[i - 1] = active_timeouts[i];

    num_active_timeouts--;
}

static void schedule_alarm ( TimestampTz  now  )  [static]

Definition at line 184 of file timeout.c.

References elog, enable_alarm, FATAL, itimerval::it_value, ITIMER_REAL, MemSet, NULL, num_active_timeouts, setitimer(), and TimestampDifference().

Referenced by disable_timeout(), disable_timeouts(), enable_timeout_after(), enable_timeout_at(), enable_timeouts(), and handle_sig_alarm().

{
    if (num_active_timeouts > 0)
    {
        struct itimerval timeval;
        long        secs;
        int         usecs;

        MemSet(&timeval, 0, sizeof(struct itimerval));

        /* Get the time remaining till the nearest pending timeout */
        TimestampDifference(now, active_timeouts[0]->fin_time,
                            &secs, &usecs);

        /*
         * It's possible that the difference is less than a microsecond;
         * ensure we don't cancel, rather than set, the interrupt.
         */
        if (secs == 0 && usecs == 0)
            usecs = 1;

        timeval.it_value.tv_sec = secs;
        timeval.it_value.tv_usec = usecs;

        /*
         * We must enable the signal handler before calling setitimer(); if we
         * did it in the other order, we'd have a race condition wherein the
         * interrupt could occur before we can set alarm_enabled, so that the
         * signal handler would fail to do anything.
         *
         * Because we didn't bother to reset the timer in disable_alarm(),
         * it's possible that a previously-set interrupt will fire between
         * enable_alarm() and setitimer().  This is safe, however.  There are
         * two possible outcomes:
         *
         * 1. The signal handler finds nothing to do (because the nearest
         * timeout event is still in the future).  It will re-set the timer
         * and return.  Then we'll overwrite the timer value with a new one.
         * This will mean that the timer fires a little later than we
         * intended, but only by the amount of time it takes for the signal
         * handler to do nothing useful, which shouldn't be much.
         *
         * 2. The signal handler executes and removes one or more timeout
         * events.  When it returns, either the queue is now empty or the
         * frontmost event is later than the one we looked at above.  So we'll
         * overwrite the timer value with one that is too soon (plus or minus
         * the signal handler's execution time), causing a useless interrupt
         * to occur.  But the handler will then re-set the timer and
         * everything will still work as expected.
         *
         * Since these cases are of very low probability (the window here
         * being quite narrow), it's not worth adding cycles to the mainline
         * code to prevent occasional wasted interrupts.
         */
        enable_alarm();

        /* Set the alarm timer */
        if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
            elog(FATAL, "could not enable SIGALRM timer: %m");
    }
}


Variable Documentation

timeout_params* volatile active_timeouts[MAX_TIMEOUTS] [static]

Definition at line 50 of file timeout.c.

volatile sig_atomic_t alarm_enabled = false [static]

Definition at line 61 of file timeout.c.

Referenced by handle_sig_alarm().

timeout_params all_timeouts[MAX_TIMEOUTS] [static]

Definition at line 42 of file timeout.c.

bool all_timeouts_initialized = false [static]
volatile int num_active_timeouts = 0 [static]