1 /*-------------------------------------------------------------------------
4 * Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
6 * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/utils/misc/timeout.c
13 *-------------------------------------------------------------------------
19 #include "storage/proc.h"
20 #include "utils/timeout.h"
21 #include "utils/timestamp.h"
24 /* Data about any one timeout reason */
25 typedef struct timeout_params
27 TimeoutId index; /* identifier of timeout reason */
29 /* volatile because it may be changed from the signal handler */
30 volatile bool indicator; /* true if timeout has occurred */
32 /* callback function for timeout, or NULL if timeout not registered */
33 timeout_handler_proc timeout_handler;
35 TimestampTz start_time; /* time that timeout was last activated */
36 TimestampTz fin_time; /* if active, time it is due to fire */
40 * List of possible timeout reasons in the order of enum TimeoutId.
42 static timeout_params all_timeouts[MAX_TIMEOUTS];
43 static bool all_timeouts_initialized = false;
46 * List of active timeouts ordered by their fin_time and priority.
47 * This list is subject to change by the interrupt handler, so it's volatile.
49 static volatile int num_active_timeouts = 0;
50 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
53 * Flag controlling whether the signal handler is allowed to do anything.
54 * We leave this "false" when we're not expecting interrupts, just in case.
56 * Note that we don't bother to reset any pending timer interrupt when we
57 * disable the signal handler; it's not really worth the cycles to do so,
58 * since the probability of the interrupt actually occurring while we have
59 * it disabled is low. See comments in schedule_alarm() about that.
61 static volatile sig_atomic_t alarm_enabled = false;
63 #define disable_alarm() (alarm_enabled = false)
64 #define enable_alarm() (alarm_enabled = true)
67 /*****************************************************************************
68 * Internal helper functions
70 * For all of these, it is caller's responsibility to protect them from
71 * interruption by the signal handler. Generally, call disable_alarm()
72 * first to prevent interruption, then update state, and last call
73 * schedule_alarm(), which will re-enable the signal handler if needed.
74 *****************************************************************************/
77 * Find the index of a given timeout reason in the active array.
78 * If it's not there, return -1.
81 find_active_timeout(TimeoutId id)
85 for (i = 0; i < num_active_timeouts; i++)
87 if (active_timeouts[i]->index == id)
95 * Insert specified timeout reason into the list of active timeouts
99 insert_timeout(TimeoutId id, int index)
103 if (index < 0 || index > num_active_timeouts)
104 elog(FATAL, "timeout index %d out of range 0..%d", index,
105 num_active_timeouts);
107 for (i = num_active_timeouts - 1; i >= index; i--)
108 active_timeouts[i + 1] = active_timeouts[i];
110 active_timeouts[index] = &all_timeouts[id];
112 num_active_timeouts++;
116 * Remove the index'th element from the timeout list.
119 remove_timeout_index(int index)
123 if (index < 0 || index >= num_active_timeouts)
124 elog(FATAL, "timeout index %d out of range 0..%d", index,
125 num_active_timeouts - 1);
127 for (i = index + 1; i < num_active_timeouts; i++)
128 active_timeouts[i - 1] = active_timeouts[i];
130 num_active_timeouts--;
134 * Enable the specified timeout reason
137 enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
141 /* Assert request is sane */
142 Assert(all_timeouts_initialized);
143 Assert(all_timeouts[id].timeout_handler != NULL);
146 * If this timeout was already active, momentarily disable it. We
147 * interpret the call as a directive to reschedule the timeout.
149 i = find_active_timeout(id);
151 remove_timeout_index(i);
154 * Find out the index where to insert the new timeout. We sort by
155 * fin_time, and for equal fin_time by priority.
157 for (i = 0; i < num_active_timeouts; i++)
159 timeout_params *old_timeout = active_timeouts[i];
161 if (fin_time < old_timeout->fin_time)
163 if (fin_time == old_timeout->fin_time && id < old_timeout->index)
168 * Mark the timeout active, and insert it into the active list.
170 all_timeouts[id].indicator = false;
171 all_timeouts[id].start_time = now;
172 all_timeouts[id].fin_time = fin_time;
174 insert_timeout(id, i);
178 * Schedule alarm for the next active timeout, if any
180 * We assume the caller has obtained the current time, or a close-enough
184 schedule_alarm(TimestampTz now)
186 if (num_active_timeouts > 0)
188 struct itimerval timeval;
192 MemSet(&timeval, 0, sizeof(struct itimerval));
194 /* Get the time remaining till the nearest pending timeout */
195 TimestampDifference(now, active_timeouts[0]->fin_time,
199 * It's possible that the difference is less than a microsecond;
200 * ensure we don't cancel, rather than set, the interrupt.
202 if (secs == 0 && usecs == 0)
205 timeval.it_value.tv_sec = secs;
206 timeval.it_value.tv_usec = usecs;
209 * We must enable the signal handler before calling setitimer(); if we
210 * did it in the other order, we'd have a race condition wherein the
211 * interrupt could occur before we can set alarm_enabled, so that the
212 * signal handler would fail to do anything.
214 * Because we didn't bother to reset the timer in disable_alarm(),
215 * it's possible that a previously-set interrupt will fire between
216 * enable_alarm() and setitimer(). This is safe, however. There are
217 * two possible outcomes:
219 * 1. The signal handler finds nothing to do (because the nearest
220 * timeout event is still in the future). It will re-set the timer
221 * and return. Then we'll overwrite the timer value with a new one.
222 * This will mean that the timer fires a little later than we
223 * intended, but only by the amount of time it takes for the signal
224 * handler to do nothing useful, which shouldn't be much.
226 * 2. The signal handler executes and removes one or more timeout
227 * events. When it returns, either the queue is now empty or the
228 * frontmost event is later than the one we looked at above. So we'll
229 * overwrite the timer value with one that is too soon (plus or minus
230 * the signal handler's execution time), causing a useless interrupt
231 * to occur. But the handler will then re-set the timer and
232 * everything will still work as expected.
234 * Since these cases are of very low probability (the window here
235 * being quite narrow), it's not worth adding cycles to the mainline
236 * code to prevent occasional wasted interrupts.
240 /* Set the alarm timer */
241 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
242 elog(FATAL, "could not enable SIGALRM timer: %m");
247 /*****************************************************************************
249 *****************************************************************************/
252 * Signal handler for SIGALRM
254 * Process any active timeout reasons and then reschedule the interrupt
258 handle_sig_alarm(SIGNAL_ARGS)
260 int save_errno = errno;
263 * SIGALRM is always cause for waking anything waiting on the process
264 * latch. Cope with MyProc not being there, as the startup process also
265 * uses this signal handler.
268 SetLatch(&MyProc->procLatch);
271 * Fire any pending timeouts, but only if we're enabled to do so.
276 * Disable alarms, just in case this platform allows signal handlers
277 * to interrupt themselves. schedule_alarm() will re-enable if
282 if (num_active_timeouts > 0)
284 TimestampTz now = GetCurrentTimestamp();
286 /* While the first pending timeout has been reached ... */
287 while (num_active_timeouts > 0 &&
288 now >= active_timeouts[0]->fin_time)
290 timeout_params *this_timeout = active_timeouts[0];
292 /* Remove it from the active list */
293 remove_timeout_index(0);
295 /* Mark it as fired */
296 this_timeout->indicator = true;
298 /* And call its handler function */
299 (*this_timeout->timeout_handler) ();
302 * The handler might not take negligible time (CheckDeadLock
303 * for instance isn't too cheap), so let's update our idea of
304 * "now" after each one.
306 now = GetCurrentTimestamp();
309 /* Done firing timeouts, so reschedule next interrupt if any */
318 /*****************************************************************************
320 *****************************************************************************/
323 * Initialize timeout module.
325 * This must be called in every process that wants to use timeouts.
327 * If the process was forked from another one that was also using this
328 * module, be sure to call this before re-enabling signals; else handlers
329 * meant to run in the parent process might get invoked in this one.
332 InitializeTimeouts(void)
336 /* Initialize, or re-initialize, all local state */
339 num_active_timeouts = 0;
341 for (i = 0; i < MAX_TIMEOUTS; i++)
343 all_timeouts[i].index = i;
344 all_timeouts[i].indicator = false;
345 all_timeouts[i].timeout_handler = NULL;
346 all_timeouts[i].start_time = 0;
347 all_timeouts[i].fin_time = 0;
350 all_timeouts_initialized = true;
352 /* Now establish the signal handler */
353 pqsignal(SIGALRM, handle_sig_alarm);
357 * Register a timeout reason
359 * For predefined timeouts, this just registers the callback function.
361 * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
362 * return a timeout ID.
365 RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
367 Assert(all_timeouts_initialized);
369 /* There's no need to disable the signal handler here. */
371 if (id >= USER_TIMEOUT)
373 /* Allocate a user-defined timeout reason */
374 for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
375 if (all_timeouts[id].timeout_handler == NULL)
377 if (id >= MAX_TIMEOUTS)
379 (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
380 errmsg("cannot add more timeout reasons")));
383 Assert(all_timeouts[id].timeout_handler == NULL);
385 all_timeouts[id].timeout_handler = handler;
391 * Enable the specified timeout to fire after the specified delay.
393 * Delay is given in milliseconds.
396 enable_timeout_after(TimeoutId id, int delay_ms)
399 TimestampTz fin_time;
401 /* Disable timeout interrupts for safety. */
404 /* Queue the timeout at the appropriate time. */
405 now = GetCurrentTimestamp();
406 fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
407 enable_timeout(id, now, fin_time);
409 /* Set the timer interrupt. */
414 * Enable the specified timeout to fire at the specified time.
416 * This is provided to support cases where there's a reason to calculate
417 * the timeout by reference to some point other than "now". If there isn't,
418 * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
421 enable_timeout_at(TimeoutId id, TimestampTz fin_time)
425 /* Disable timeout interrupts for safety. */
428 /* Queue the timeout at the appropriate time. */
429 now = GetCurrentTimestamp();
430 enable_timeout(id, now, fin_time);
432 /* Set the timer interrupt. */
437 * Enable multiple timeouts at once.
439 * This works like calling enable_timeout_after() and/or enable_timeout_at()
440 * multiple times. Use this to reduce the number of GetCurrentTimestamp()
441 * and setitimer() calls needed to establish multiple timeouts.
444 enable_timeouts(const EnableTimeoutParams *timeouts, int count)
449 /* Disable timeout interrupts for safety. */
452 /* Queue the timeout(s) at the appropriate times. */
453 now = GetCurrentTimestamp();
455 for (i = 0; i < count; i++)
457 TimeoutId id = timeouts[i].id;
458 TimestampTz fin_time;
460 switch (timeouts[i].type)
463 fin_time = TimestampTzPlusMilliseconds(now,
464 timeouts[i].delay_ms);
465 enable_timeout(id, now, fin_time);
469 enable_timeout(id, now, timeouts[i].fin_time);
473 elog(ERROR, "unrecognized timeout type %d",
474 (int) timeouts[i].type);
479 /* Set the timer interrupt. */
484 * Cancel the specified timeout.
486 * The timeout's I've-been-fired indicator is reset,
487 * unless keep_indicator is true.
489 * When a timeout is canceled, any other active timeout remains in force.
490 * It's not an error to disable a timeout that is not enabled.
493 disable_timeout(TimeoutId id, bool keep_indicator)
497 /* Assert request is sane */
498 Assert(all_timeouts_initialized);
499 Assert(all_timeouts[id].timeout_handler != NULL);
501 /* Disable timeout interrupts for safety. */
504 /* Find the timeout and remove it from the active list. */
505 i = find_active_timeout(id);
507 remove_timeout_index(i);
509 /* Mark it inactive, whether it was active or not. */
511 all_timeouts[id].indicator = false;
513 /* Reschedule the interrupt, if any timeouts remain active. */
514 if (num_active_timeouts > 0)
515 schedule_alarm(GetCurrentTimestamp());
519 * Cancel multiple timeouts at once.
521 * The timeouts' I've-been-fired indicators are reset,
522 * unless timeouts[i].keep_indicator is true.
524 * This works like calling disable_timeout() multiple times.
525 * Use this to reduce the number of GetCurrentTimestamp()
526 * and setitimer() calls needed to cancel multiple timeouts.
529 disable_timeouts(const DisableTimeoutParams *timeouts, int count)
533 Assert(all_timeouts_initialized);
535 /* Disable timeout interrupts for safety. */
538 /* Cancel the timeout(s). */
539 for (i = 0; i < count; i++)
541 TimeoutId id = timeouts[i].id;
544 Assert(all_timeouts[id].timeout_handler != NULL);
546 idx = find_active_timeout(id);
548 remove_timeout_index(idx);
550 if (!timeouts[i].keep_indicator)
551 all_timeouts[id].indicator = false;
554 /* Reschedule the interrupt, if any timeouts remain active. */
555 if (num_active_timeouts > 0)
556 schedule_alarm(GetCurrentTimestamp());
560 * Disable SIGALRM and remove all timeouts from the active list,
561 * and optionally reset their timeout indicators.
564 disable_all_timeouts(bool keep_indicators)
569 * Only bother to reset the timer if we think it's active. We could just
570 * let the interrupt happen anyway, but it's probably a bit cheaper to do
571 * setitimer() than to let the useless interrupt happen.
573 if (num_active_timeouts > 0)
575 struct itimerval timeval;
577 MemSet(&timeval, 0, sizeof(struct itimerval));
578 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
579 elog(FATAL, "could not disable SIGALRM timer: %m");
582 num_active_timeouts = 0;
584 if (!keep_indicators)
588 for (i = 0; i < MAX_TIMEOUTS; i++)
589 all_timeouts[i].indicator = false;
594 * Return the timeout's I've-been-fired indicator
596 * If reset_indicator is true, reset the indicator when returning true.
597 * To avoid missing timeouts due to race conditions, we are careful not to
598 * reset the indicator when returning false.
601 get_timeout_indicator(TimeoutId id, bool reset_indicator)
603 if (all_timeouts[id].indicator)
606 all_timeouts[id].indicator = false;
613 * Return the time when the timeout was most recently activated
615 * Note: will return 0 if timeout has never been activated in this process.
616 * However, we do *not* reset the start_time when a timeout occurs, so as
617 * not to create a race condition if SIGALRM fires just as some code is
618 * about to fetch the value.
621 get_timeout_start_time(TimeoutId id)
623 return all_timeouts[id].start_time;