Header And Logo

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

timeout.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * timeout.c
00004  *    Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/utils/misc/timeout.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <sys/time.h>
00018 
00019 #include "storage/proc.h"
00020 #include "utils/timeout.h"
00021 #include "utils/timestamp.h"
00022 
00023 
00024 /* Data about any one timeout reason */
00025 typedef struct timeout_params
00026 {
00027     TimeoutId   index;          /* identifier of timeout reason */
00028 
00029     /* volatile because it may be changed from the signal handler */
00030     volatile bool indicator;    /* true if timeout has occurred */
00031 
00032     /* callback function for timeout, or NULL if timeout not registered */
00033     timeout_handler_proc timeout_handler;
00034 
00035     TimestampTz start_time;     /* time that timeout was last activated */
00036     TimestampTz fin_time;       /* if active, time it is due to fire */
00037 } timeout_params;
00038 
00039 /*
00040  * List of possible timeout reasons in the order of enum TimeoutId.
00041  */
00042 static timeout_params all_timeouts[MAX_TIMEOUTS];
00043 static bool all_timeouts_initialized = false;
00044 
00045 /*
00046  * List of active timeouts ordered by their fin_time and priority.
00047  * This list is subject to change by the interrupt handler, so it's volatile.
00048  */
00049 static volatile int num_active_timeouts = 0;
00050 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
00051 
00052 /*
00053  * Flag controlling whether the signal handler is allowed to do anything.
00054  * We leave this "false" when we're not expecting interrupts, just in case.
00055  *
00056  * Note that we don't bother to reset any pending timer interrupt when we
00057  * disable the signal handler; it's not really worth the cycles to do so,
00058  * since the probability of the interrupt actually occurring while we have
00059  * it disabled is low.  See comments in schedule_alarm() about that.
00060  */
00061 static volatile sig_atomic_t alarm_enabled = false;
00062 
00063 #define disable_alarm() (alarm_enabled = false)
00064 #define enable_alarm()  (alarm_enabled = true)
00065 
00066 
00067 /*****************************************************************************
00068  * Internal helper functions
00069  *
00070  * For all of these, it is caller's responsibility to protect them from
00071  * interruption by the signal handler.  Generally, call disable_alarm()
00072  * first to prevent interruption, then update state, and last call
00073  * schedule_alarm(), which will re-enable the signal handler if needed.
00074  *****************************************************************************/
00075 
00076 /*
00077  * Find the index of a given timeout reason in the active array.
00078  * If it's not there, return -1.
00079  */
00080 static int
00081 find_active_timeout(TimeoutId id)
00082 {
00083     int         i;
00084 
00085     for (i = 0; i < num_active_timeouts; i++)
00086     {
00087         if (active_timeouts[i]->index == id)
00088             return i;
00089     }
00090 
00091     return -1;
00092 }
00093 
00094 /*
00095  * Insert specified timeout reason into the list of active timeouts
00096  * at the given index.
00097  */
00098 static void
00099 insert_timeout(TimeoutId id, int index)
00100 {
00101     int         i;
00102 
00103     if (index < 0 || index > num_active_timeouts)
00104         elog(FATAL, "timeout index %d out of range 0..%d", index,
00105              num_active_timeouts);
00106 
00107     for (i = num_active_timeouts - 1; i >= index; i--)
00108         active_timeouts[i + 1] = active_timeouts[i];
00109 
00110     active_timeouts[index] = &all_timeouts[id];
00111 
00112     num_active_timeouts++;
00113 }
00114 
00115 /*
00116  * Remove the index'th element from the timeout list.
00117  */
00118 static void
00119 remove_timeout_index(int index)
00120 {
00121     int         i;
00122 
00123     if (index < 0 || index >= num_active_timeouts)
00124         elog(FATAL, "timeout index %d out of range 0..%d", index,
00125              num_active_timeouts - 1);
00126 
00127     for (i = index + 1; i < num_active_timeouts; i++)
00128         active_timeouts[i - 1] = active_timeouts[i];
00129 
00130     num_active_timeouts--;
00131 }
00132 
00133 /*
00134  * Enable the specified timeout reason
00135  */
00136 static void
00137 enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
00138 {
00139     int         i;
00140 
00141     /* Assert request is sane */
00142     Assert(all_timeouts_initialized);
00143     Assert(all_timeouts[id].timeout_handler != NULL);
00144 
00145     /*
00146      * If this timeout was already active, momentarily disable it.  We
00147      * interpret the call as a directive to reschedule the timeout.
00148      */
00149     i = find_active_timeout(id);
00150     if (i >= 0)
00151         remove_timeout_index(i);
00152 
00153     /*
00154      * Find out the index where to insert the new timeout.  We sort by
00155      * fin_time, and for equal fin_time by priority.
00156      */
00157     for (i = 0; i < num_active_timeouts; i++)
00158     {
00159         timeout_params *old_timeout = active_timeouts[i];
00160 
00161         if (fin_time < old_timeout->fin_time)
00162             break;
00163         if (fin_time == old_timeout->fin_time && id < old_timeout->index)
00164             break;
00165     }
00166 
00167     /*
00168      * Mark the timeout active, and insert it into the active list.
00169      */
00170     all_timeouts[id].indicator = false;
00171     all_timeouts[id].start_time = now;
00172     all_timeouts[id].fin_time = fin_time;
00173 
00174     insert_timeout(id, i);
00175 }
00176 
00177 /*
00178  * Schedule alarm for the next active timeout, if any
00179  *
00180  * We assume the caller has obtained the current time, or a close-enough
00181  * approximation.
00182  */
00183 static void
00184 schedule_alarm(TimestampTz now)
00185 {
00186     if (num_active_timeouts > 0)
00187     {
00188         struct itimerval timeval;
00189         long        secs;
00190         int         usecs;
00191 
00192         MemSet(&timeval, 0, sizeof(struct itimerval));
00193 
00194         /* Get the time remaining till the nearest pending timeout */
00195         TimestampDifference(now, active_timeouts[0]->fin_time,
00196                             &secs, &usecs);
00197 
00198         /*
00199          * It's possible that the difference is less than a microsecond;
00200          * ensure we don't cancel, rather than set, the interrupt.
00201          */
00202         if (secs == 0 && usecs == 0)
00203             usecs = 1;
00204 
00205         timeval.it_value.tv_sec = secs;
00206         timeval.it_value.tv_usec = usecs;
00207 
00208         /*
00209          * We must enable the signal handler before calling setitimer(); if we
00210          * did it in the other order, we'd have a race condition wherein the
00211          * interrupt could occur before we can set alarm_enabled, so that the
00212          * signal handler would fail to do anything.
00213          *
00214          * Because we didn't bother to reset the timer in disable_alarm(),
00215          * it's possible that a previously-set interrupt will fire between
00216          * enable_alarm() and setitimer().  This is safe, however.  There are
00217          * two possible outcomes:
00218          *
00219          * 1. The signal handler finds nothing to do (because the nearest
00220          * timeout event is still in the future).  It will re-set the timer
00221          * and return.  Then we'll overwrite the timer value with a new one.
00222          * This will mean that the timer fires a little later than we
00223          * intended, but only by the amount of time it takes for the signal
00224          * handler to do nothing useful, which shouldn't be much.
00225          *
00226          * 2. The signal handler executes and removes one or more timeout
00227          * events.  When it returns, either the queue is now empty or the
00228          * frontmost event is later than the one we looked at above.  So we'll
00229          * overwrite the timer value with one that is too soon (plus or minus
00230          * the signal handler's execution time), causing a useless interrupt
00231          * to occur.  But the handler will then re-set the timer and
00232          * everything will still work as expected.
00233          *
00234          * Since these cases are of very low probability (the window here
00235          * being quite narrow), it's not worth adding cycles to the mainline
00236          * code to prevent occasional wasted interrupts.
00237          */
00238         enable_alarm();
00239 
00240         /* Set the alarm timer */
00241         if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
00242             elog(FATAL, "could not enable SIGALRM timer: %m");
00243     }
00244 }
00245 
00246 
00247 /*****************************************************************************
00248  * Signal handler
00249  *****************************************************************************/
00250 
00251 /*
00252  * Signal handler for SIGALRM
00253  *
00254  * Process any active timeout reasons and then reschedule the interrupt
00255  * as needed.
00256  */
00257 static void
00258 handle_sig_alarm(SIGNAL_ARGS)
00259 {
00260     int         save_errno = errno;
00261 
00262     /*
00263      * SIGALRM is always cause for waking anything waiting on the process
00264      * latch.  Cope with MyProc not being there, as the startup process also
00265      * uses this signal handler.
00266      */
00267     if (MyProc)
00268         SetLatch(&MyProc->procLatch);
00269 
00270     /*
00271      * Fire any pending timeouts, but only if we're enabled to do so.
00272      */
00273     if (alarm_enabled)
00274     {
00275         /*
00276          * Disable alarms, just in case this platform allows signal handlers
00277          * to interrupt themselves.  schedule_alarm() will re-enable if
00278          * appropriate.
00279          */
00280         disable_alarm();
00281 
00282         if (num_active_timeouts > 0)
00283         {
00284             TimestampTz now = GetCurrentTimestamp();
00285 
00286             /* While the first pending timeout has been reached ... */
00287             while (num_active_timeouts > 0 &&
00288                    now >= active_timeouts[0]->fin_time)
00289             {
00290                 timeout_params *this_timeout = active_timeouts[0];
00291 
00292                 /* Remove it from the active list */
00293                 remove_timeout_index(0);
00294 
00295                 /* Mark it as fired */
00296                 this_timeout->indicator = true;
00297 
00298                 /* And call its handler function */
00299                 (*this_timeout->timeout_handler) ();
00300 
00301                 /*
00302                  * The handler might not take negligible time (CheckDeadLock
00303                  * for instance isn't too cheap), so let's update our idea of
00304                  * "now" after each one.
00305                  */
00306                 now = GetCurrentTimestamp();
00307             }
00308 
00309             /* Done firing timeouts, so reschedule next interrupt if any */
00310             schedule_alarm(now);
00311         }
00312     }
00313 
00314     errno = save_errno;
00315 }
00316 
00317 
00318 /*****************************************************************************
00319  * Public API
00320  *****************************************************************************/
00321 
00322 /*
00323  * Initialize timeout module.
00324  *
00325  * This must be called in every process that wants to use timeouts.
00326  *
00327  * If the process was forked from another one that was also using this
00328  * module, be sure to call this before re-enabling signals; else handlers
00329  * meant to run in the parent process might get invoked in this one.
00330  */
00331 void
00332 InitializeTimeouts(void)
00333 {
00334     int         i;
00335 
00336     /* Initialize, or re-initialize, all local state */
00337     disable_alarm();
00338 
00339     num_active_timeouts = 0;
00340 
00341     for (i = 0; i < MAX_TIMEOUTS; i++)
00342     {
00343         all_timeouts[i].index = i;
00344         all_timeouts[i].indicator = false;
00345         all_timeouts[i].timeout_handler = NULL;
00346         all_timeouts[i].start_time = 0;
00347         all_timeouts[i].fin_time = 0;
00348     }
00349 
00350     all_timeouts_initialized = true;
00351 
00352     /* Now establish the signal handler */
00353     pqsignal(SIGALRM, handle_sig_alarm);
00354 }
00355 
00356 /*
00357  * Register a timeout reason
00358  *
00359  * For predefined timeouts, this just registers the callback function.
00360  *
00361  * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
00362  * return a timeout ID.
00363  */
00364 TimeoutId
00365 RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
00366 {
00367     Assert(all_timeouts_initialized);
00368 
00369     /* There's no need to disable the signal handler here. */
00370 
00371     if (id >= USER_TIMEOUT)
00372     {
00373         /* Allocate a user-defined timeout reason */
00374         for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
00375             if (all_timeouts[id].timeout_handler == NULL)
00376                 break;
00377         if (id >= MAX_TIMEOUTS)
00378             ereport(FATAL,
00379                     (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
00380                      errmsg("cannot add more timeout reasons")));
00381     }
00382 
00383     Assert(all_timeouts[id].timeout_handler == NULL);
00384 
00385     all_timeouts[id].timeout_handler = handler;
00386 
00387     return id;
00388 }
00389 
00390 /*
00391  * Enable the specified timeout to fire after the specified delay.
00392  *
00393  * Delay is given in milliseconds.
00394  */
00395 void
00396 enable_timeout_after(TimeoutId id, int delay_ms)
00397 {
00398     TimestampTz now;
00399     TimestampTz fin_time;
00400 
00401     /* Disable timeout interrupts for safety. */
00402     disable_alarm();
00403 
00404     /* Queue the timeout at the appropriate time. */
00405     now = GetCurrentTimestamp();
00406     fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
00407     enable_timeout(id, now, fin_time);
00408 
00409     /* Set the timer interrupt. */
00410     schedule_alarm(now);
00411 }
00412 
00413 /*
00414  * Enable the specified timeout to fire at the specified time.
00415  *
00416  * This is provided to support cases where there's a reason to calculate
00417  * the timeout by reference to some point other than "now".  If there isn't,
00418  * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
00419  */
00420 void
00421 enable_timeout_at(TimeoutId id, TimestampTz fin_time)
00422 {
00423     TimestampTz now;
00424 
00425     /* Disable timeout interrupts for safety. */
00426     disable_alarm();
00427 
00428     /* Queue the timeout at the appropriate time. */
00429     now = GetCurrentTimestamp();
00430     enable_timeout(id, now, fin_time);
00431 
00432     /* Set the timer interrupt. */
00433     schedule_alarm(now);
00434 }
00435 
00436 /*
00437  * Enable multiple timeouts at once.
00438  *
00439  * This works like calling enable_timeout_after() and/or enable_timeout_at()
00440  * multiple times.  Use this to reduce the number of GetCurrentTimestamp()
00441  * and setitimer() calls needed to establish multiple timeouts.
00442  */
00443 void
00444 enable_timeouts(const EnableTimeoutParams *timeouts, int count)
00445 {
00446     TimestampTz now;
00447     int         i;
00448 
00449     /* Disable timeout interrupts for safety. */
00450     disable_alarm();
00451 
00452     /* Queue the timeout(s) at the appropriate times. */
00453     now = GetCurrentTimestamp();
00454 
00455     for (i = 0; i < count; i++)
00456     {
00457         TimeoutId   id = timeouts[i].id;
00458         TimestampTz fin_time;
00459 
00460         switch (timeouts[i].type)
00461         {
00462             case TMPARAM_AFTER:
00463                 fin_time = TimestampTzPlusMilliseconds(now,
00464                                                        timeouts[i].delay_ms);
00465                 enable_timeout(id, now, fin_time);
00466                 break;
00467 
00468             case TMPARAM_AT:
00469                 enable_timeout(id, now, timeouts[i].fin_time);
00470                 break;
00471 
00472             default:
00473                 elog(ERROR, "unrecognized timeout type %d",
00474                      (int) timeouts[i].type);
00475                 break;
00476         }
00477     }
00478 
00479     /* Set the timer interrupt. */
00480     schedule_alarm(now);
00481 }
00482 
00483 /*
00484  * Cancel the specified timeout.
00485  *
00486  * The timeout's I've-been-fired indicator is reset,
00487  * unless keep_indicator is true.
00488  *
00489  * When a timeout is canceled, any other active timeout remains in force.
00490  * It's not an error to disable a timeout that is not enabled.
00491  */
00492 void
00493 disable_timeout(TimeoutId id, bool keep_indicator)
00494 {
00495     int         i;
00496 
00497     /* Assert request is sane */
00498     Assert(all_timeouts_initialized);
00499     Assert(all_timeouts[id].timeout_handler != NULL);
00500 
00501     /* Disable timeout interrupts for safety. */
00502     disable_alarm();
00503 
00504     /* Find the timeout and remove it from the active list. */
00505     i = find_active_timeout(id);
00506     if (i >= 0)
00507         remove_timeout_index(i);
00508 
00509     /* Mark it inactive, whether it was active or not. */
00510     if (!keep_indicator)
00511         all_timeouts[id].indicator = false;
00512 
00513     /* Reschedule the interrupt, if any timeouts remain active. */
00514     if (num_active_timeouts > 0)
00515         schedule_alarm(GetCurrentTimestamp());
00516 }
00517 
00518 /*
00519  * Cancel multiple timeouts at once.
00520  *
00521  * The timeouts' I've-been-fired indicators are reset,
00522  * unless timeouts[i].keep_indicator is true.
00523  *
00524  * This works like calling disable_timeout() multiple times.
00525  * Use this to reduce the number of GetCurrentTimestamp()
00526  * and setitimer() calls needed to cancel multiple timeouts.
00527  */
00528 void
00529 disable_timeouts(const DisableTimeoutParams *timeouts, int count)
00530 {
00531     int         i;
00532 
00533     Assert(all_timeouts_initialized);
00534 
00535     /* Disable timeout interrupts for safety. */
00536     disable_alarm();
00537 
00538     /* Cancel the timeout(s). */
00539     for (i = 0; i < count; i++)
00540     {
00541         TimeoutId   id = timeouts[i].id;
00542         int         idx;
00543 
00544         Assert(all_timeouts[id].timeout_handler != NULL);
00545 
00546         idx = find_active_timeout(id);
00547         if (idx >= 0)
00548             remove_timeout_index(idx);
00549 
00550         if (!timeouts[i].keep_indicator)
00551             all_timeouts[id].indicator = false;
00552     }
00553 
00554     /* Reschedule the interrupt, if any timeouts remain active. */
00555     if (num_active_timeouts > 0)
00556         schedule_alarm(GetCurrentTimestamp());
00557 }
00558 
00559 /*
00560  * Disable SIGALRM and remove all timeouts from the active list,
00561  * and optionally reset their timeout indicators.
00562  */
00563 void
00564 disable_all_timeouts(bool keep_indicators)
00565 {
00566     disable_alarm();
00567 
00568     /*
00569      * Only bother to reset the timer if we think it's active.  We could just
00570      * let the interrupt happen anyway, but it's probably a bit cheaper to do
00571      * setitimer() than to let the useless interrupt happen.
00572      */
00573     if (num_active_timeouts > 0)
00574     {
00575         struct itimerval timeval;
00576 
00577         MemSet(&timeval, 0, sizeof(struct itimerval));
00578         if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
00579             elog(FATAL, "could not disable SIGALRM timer: %m");
00580     }
00581 
00582     num_active_timeouts = 0;
00583 
00584     if (!keep_indicators)
00585     {
00586         int         i;
00587 
00588         for (i = 0; i < MAX_TIMEOUTS; i++)
00589             all_timeouts[i].indicator = false;
00590     }
00591 }
00592 
00593 /*
00594  * Return the timeout's I've-been-fired indicator
00595  *
00596  * If reset_indicator is true, reset the indicator when returning true.
00597  * To avoid missing timeouts due to race conditions, we are careful not to
00598  * reset the indicator when returning false.
00599  */
00600 bool
00601 get_timeout_indicator(TimeoutId id, bool reset_indicator)
00602 {
00603     if (all_timeouts[id].indicator)
00604     {
00605         if (reset_indicator)
00606             all_timeouts[id].indicator = false;
00607         return true;
00608     }
00609     return false;
00610 }
00611 
00612 /*
00613  * Return the time when the timeout was most recently activated
00614  *
00615  * Note: will return 0 if timeout has never been activated in this process.
00616  * However, we do *not* reset the start_time when a timeout occurs, so as
00617  * not to create a race condition if SIGALRM fires just as some code is
00618  * about to fetch the value.
00619  */
00620 TimestampTz
00621 get_timeout_start_time(TimeoutId id)
00622 {
00623     return all_timeouts[id].start_time;
00624 }