Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * timeout.c
4 : * Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/misc/timeout.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <sys/time.h>
18 :
19 : #include "miscadmin.h"
20 : #include "storage/proc.h"
21 : #include "utils/timeout.h"
22 : #include "utils/timestamp.h"
23 :
24 :
25 : /* Data about any one timeout reason */
26 : typedef struct timeout_params
27 : {
28 : TimeoutId index; /* identifier of timeout reason */
29 :
30 : /* volatile because it may be changed from the signal handler */
31 : volatile bool indicator; /* true if timeout has occurred */
32 :
33 : /* callback function for timeout, or NULL if timeout not registered */
34 : timeout_handler_proc timeout_handler;
35 :
36 : TimestampTz start_time; /* time that timeout was last activated */
37 : TimestampTz fin_time; /* time it is, or was last, due to fire */
38 : } timeout_params;
39 :
40 : /*
41 : * List of possible timeout reasons in the order of enum TimeoutId.
42 : */
43 : static timeout_params all_timeouts[MAX_TIMEOUTS];
44 : static bool all_timeouts_initialized = false;
45 :
46 : /*
47 : * List of active timeouts ordered by their fin_time and priority.
48 : * This list is subject to change by the interrupt handler, so it's volatile.
49 : */
50 : static volatile int num_active_timeouts = 0;
51 : static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
52 :
53 : /*
54 : * Flag controlling whether the signal handler is allowed to do anything.
55 : * We leave this "false" when we're not expecting interrupts, just in case.
56 : *
57 : * Note that we don't bother to reset any pending timer interrupt when we
58 : * disable the signal handler; it's not really worth the cycles to do so,
59 : * since the probability of the interrupt actually occurring while we have
60 : * it disabled is low. See comments in schedule_alarm() about that.
61 : */
62 : static volatile sig_atomic_t alarm_enabled = false;
63 :
64 : #define disable_alarm() (alarm_enabled = false)
65 : #define enable_alarm() (alarm_enabled = true)
66 :
67 :
68 : /*****************************************************************************
69 : * Internal helper functions
70 : *
71 : * For all of these, it is caller's responsibility to protect them from
72 : * interruption by the signal handler. Generally, call disable_alarm()
73 : * first to prevent interruption, then update state, and last call
74 : * schedule_alarm(), which will re-enable the signal handler if needed.
75 : *****************************************************************************/
76 :
77 : /*
78 : * Find the index of a given timeout reason in the active array.
79 : * If it's not there, return -1.
80 : */
81 : static int
82 52200 : find_active_timeout(TimeoutId id)
83 : {
84 : int i;
85 :
86 52200 : for (i = 0; i < num_active_timeouts; i++)
87 : {
88 448 : if (active_timeouts[i]->index == id)
89 448 : return i;
90 : }
91 :
92 51752 : return -1;
93 : }
94 :
95 : /*
96 : * Insert specified timeout reason into the list of active timeouts
97 : * at the given index.
98 : */
99 : static void
100 449 : insert_timeout(TimeoutId id, int index)
101 : {
102 : int i;
103 :
104 449 : if (index < 0 || index > num_active_timeouts)
105 0 : elog(FATAL, "timeout index %d out of range 0..%d", index,
106 : num_active_timeouts);
107 :
108 449 : for (i = num_active_timeouts - 1; i >= index; i--)
109 0 : active_timeouts[i + 1] = active_timeouts[i];
110 :
111 449 : active_timeouts[index] = &all_timeouts[id];
112 :
113 449 : num_active_timeouts++;
114 449 : }
115 :
116 : /*
117 : * Remove the index'th element from the timeout list.
118 : */
119 : static void
120 449 : remove_timeout_index(int index)
121 : {
122 : int i;
123 :
124 449 : if (index < 0 || index >= num_active_timeouts)
125 0 : elog(FATAL, "timeout index %d out of range 0..%d", index,
126 : num_active_timeouts - 1);
127 :
128 449 : for (i = index + 1; i < num_active_timeouts; i++)
129 0 : active_timeouts[i - 1] = active_timeouts[i];
130 :
131 449 : num_active_timeouts--;
132 449 : }
133 :
134 : /*
135 : * Enable the specified timeout reason
136 : */
137 : static void
138 449 : enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
139 : {
140 : int i;
141 :
142 : /* Assert request is sane */
143 449 : Assert(all_timeouts_initialized);
144 449 : Assert(all_timeouts[id].timeout_handler != NULL);
145 :
146 : /*
147 : * If this timeout was already active, momentarily disable it. We
148 : * interpret the call as a directive to reschedule the timeout.
149 : */
150 449 : i = find_active_timeout(id);
151 449 : if (i >= 0)
152 0 : remove_timeout_index(i);
153 :
154 : /*
155 : * Find out the index where to insert the new timeout. We sort by
156 : * fin_time, and for equal fin_time by priority.
157 : */
158 449 : for (i = 0; i < num_active_timeouts; i++)
159 : {
160 0 : timeout_params *old_timeout = active_timeouts[i];
161 :
162 0 : if (fin_time < old_timeout->fin_time)
163 0 : break;
164 0 : if (fin_time == old_timeout->fin_time && id < old_timeout->index)
165 0 : break;
166 : }
167 :
168 : /*
169 : * Mark the timeout active, and insert it into the active list.
170 : */
171 449 : all_timeouts[id].indicator = false;
172 449 : all_timeouts[id].start_time = now;
173 449 : all_timeouts[id].fin_time = fin_time;
174 :
175 449 : insert_timeout(id, i);
176 449 : }
177 :
178 : /*
179 : * Schedule alarm for the next active timeout, if any
180 : *
181 : * We assume the caller has obtained the current time, or a close-enough
182 : * approximation.
183 : */
184 : static void
185 450 : schedule_alarm(TimestampTz now)
186 : {
187 450 : if (num_active_timeouts > 0)
188 : {
189 : struct itimerval timeval;
190 : long secs;
191 : int usecs;
192 :
193 449 : MemSet(&timeval, 0, sizeof(struct itimerval));
194 :
195 : /* Get the time remaining till the nearest pending timeout */
196 449 : TimestampDifference(now, active_timeouts[0]->fin_time,
197 : &secs, &usecs);
198 :
199 : /*
200 : * It's possible that the difference is less than a microsecond;
201 : * ensure we don't cancel, rather than set, the interrupt.
202 : */
203 449 : if (secs == 0 && usecs == 0)
204 0 : usecs = 1;
205 :
206 449 : timeval.it_value.tv_sec = secs;
207 449 : timeval.it_value.tv_usec = usecs;
208 :
209 : /*
210 : * We must enable the signal handler before calling setitimer(); if we
211 : * did it in the other order, we'd have a race condition wherein the
212 : * interrupt could occur before we can set alarm_enabled, so that the
213 : * signal handler would fail to do anything.
214 : *
215 : * Because we didn't bother to reset the timer in disable_alarm(),
216 : * it's possible that a previously-set interrupt will fire between
217 : * enable_alarm() and setitimer(). This is safe, however. There are
218 : * two possible outcomes:
219 : *
220 : * 1. The signal handler finds nothing to do (because the nearest
221 : * timeout event is still in the future). It will re-set the timer
222 : * and return. Then we'll overwrite the timer value with a new one.
223 : * This will mean that the timer fires a little later than we
224 : * intended, but only by the amount of time it takes for the signal
225 : * handler to do nothing useful, which shouldn't be much.
226 : *
227 : * 2. The signal handler executes and removes one or more timeout
228 : * events. When it returns, either the queue is now empty or the
229 : * frontmost event is later than the one we looked at above. So we'll
230 : * overwrite the timer value with one that is too soon (plus or minus
231 : * the signal handler's execution time), causing a useless interrupt
232 : * to occur. But the handler will then re-set the timer and
233 : * everything will still work as expected.
234 : *
235 : * Since these cases are of very low probability (the window here
236 : * being quite narrow), it's not worth adding cycles to the mainline
237 : * code to prevent occasional wasted interrupts.
238 : */
239 449 : enable_alarm();
240 :
241 : /* Set the alarm timer */
242 449 : if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
243 0 : elog(FATAL, "could not enable SIGALRM timer: %m");
244 : }
245 450 : }
246 :
247 :
248 : /*****************************************************************************
249 : * Signal handler
250 : *****************************************************************************/
251 :
252 : /*
253 : * Signal handler for SIGALRM
254 : *
255 : * Process any active timeout reasons and then reschedule the interrupt
256 : * as needed.
257 : */
258 : static void
259 13 : handle_sig_alarm(SIGNAL_ARGS)
260 : {
261 13 : int save_errno = errno;
262 :
263 : /*
264 : * Bump the holdoff counter, to make sure nothing we call will process
265 : * interrupts directly. No timeout handler should do that, but these
266 : * failures are hard to debug, so better be sure.
267 : */
268 13 : HOLD_INTERRUPTS();
269 :
270 : /*
271 : * SIGALRM is always cause for waking anything waiting on the process
272 : * latch.
273 : */
274 13 : SetLatch(MyLatch);
275 :
276 : /*
277 : * Fire any pending timeouts, but only if we're enabled to do so.
278 : */
279 13 : if (alarm_enabled)
280 : {
281 : /*
282 : * Disable alarms, just in case this platform allows signal handlers
283 : * to interrupt themselves. schedule_alarm() will re-enable if
284 : * appropriate.
285 : */
286 1 : disable_alarm();
287 :
288 1 : if (num_active_timeouts > 0)
289 : {
290 1 : TimestampTz now = GetCurrentTimestamp();
291 :
292 : /* While the first pending timeout has been reached ... */
293 4 : while (num_active_timeouts > 0 &&
294 1 : now >= active_timeouts[0]->fin_time)
295 : {
296 1 : timeout_params *this_timeout = active_timeouts[0];
297 :
298 : /* Remove it from the active list */
299 1 : remove_timeout_index(0);
300 :
301 : /* Mark it as fired */
302 1 : this_timeout->indicator = true;
303 :
304 : /* And call its handler function */
305 1 : (*this_timeout->timeout_handler) ();
306 :
307 : /*
308 : * The handler might not take negligible time (CheckDeadLock
309 : * for instance isn't too cheap), so let's update our idea of
310 : * "now" after each one.
311 : */
312 1 : now = GetCurrentTimestamp();
313 : }
314 :
315 : /* Done firing timeouts, so reschedule next interrupt if any */
316 1 : schedule_alarm(now);
317 : }
318 : }
319 :
320 13 : RESUME_INTERRUPTS();
321 :
322 13 : errno = save_errno;
323 13 : }
324 :
325 :
326 : /*****************************************************************************
327 : * Public API
328 : *****************************************************************************/
329 :
330 : /*
331 : * Initialize timeout module.
332 : *
333 : * This must be called in every process that wants to use timeouts.
334 : *
335 : * If the process was forked from another one that was also using this
336 : * module, be sure to call this before re-enabling signals; else handlers
337 : * meant to run in the parent process might get invoked in this one.
338 : */
339 : void
340 553 : InitializeTimeouts(void)
341 : {
342 : int i;
343 :
344 : /* Initialize, or re-initialize, all local state */
345 553 : disable_alarm();
346 :
347 553 : num_active_timeouts = 0;
348 :
349 9401 : for (i = 0; i < MAX_TIMEOUTS; i++)
350 : {
351 8848 : all_timeouts[i].index = i;
352 8848 : all_timeouts[i].indicator = false;
353 8848 : all_timeouts[i].timeout_handler = NULL;
354 8848 : all_timeouts[i].start_time = 0;
355 8848 : all_timeouts[i].fin_time = 0;
356 : }
357 :
358 553 : all_timeouts_initialized = true;
359 :
360 : /* Now establish the signal handler */
361 553 : pqsignal(SIGALRM, handle_sig_alarm);
362 553 : }
363 :
364 : /*
365 : * Register a timeout reason
366 : *
367 : * For predefined timeouts, this just registers the callback function.
368 : *
369 : * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
370 : * return a timeout ID.
371 : */
372 : TimeoutId
373 1566 : RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
374 : {
375 1566 : Assert(all_timeouts_initialized);
376 :
377 : /* There's no need to disable the signal handler here. */
378 :
379 1566 : if (id >= USER_TIMEOUT)
380 : {
381 : /* Allocate a user-defined timeout reason */
382 0 : for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
383 0 : if (all_timeouts[id].timeout_handler == NULL)
384 0 : break;
385 0 : if (id >= MAX_TIMEOUTS)
386 0 : ereport(FATAL,
387 : (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
388 : errmsg("cannot add more timeout reasons")));
389 : }
390 :
391 1566 : Assert(all_timeouts[id].timeout_handler == NULL);
392 :
393 1566 : all_timeouts[id].timeout_handler = handler;
394 :
395 1566 : return id;
396 : }
397 :
398 : /*
399 : * Reschedule any pending SIGALRM interrupt.
400 : *
401 : * This can be used during error recovery in case query cancel resulted in loss
402 : * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it
403 : * could do anything). But note it's not necessary if any of the public
404 : * enable_ or disable_timeout functions are called in the same area, since
405 : * those all do schedule_alarm() internally if needed.
406 : */
407 : void
408 3601 : reschedule_timeouts(void)
409 : {
410 : /* For flexibility, allow this to be called before we're initialized. */
411 3601 : if (!all_timeouts_initialized)
412 3601 : return;
413 :
414 : /* Disable timeout interrupts for safety. */
415 3601 : disable_alarm();
416 :
417 : /* Reschedule the interrupt, if any timeouts remain active. */
418 3601 : if (num_active_timeouts > 0)
419 0 : schedule_alarm(GetCurrentTimestamp());
420 : }
421 :
422 : /*
423 : * Enable the specified timeout to fire after the specified delay.
424 : *
425 : * Delay is given in milliseconds.
426 : */
427 : void
428 449 : enable_timeout_after(TimeoutId id, int delay_ms)
429 : {
430 : TimestampTz now;
431 : TimestampTz fin_time;
432 :
433 : /* Disable timeout interrupts for safety. */
434 449 : disable_alarm();
435 :
436 : /* Queue the timeout at the appropriate time. */
437 449 : now = GetCurrentTimestamp();
438 449 : fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
439 449 : enable_timeout(id, now, fin_time);
440 :
441 : /* Set the timer interrupt. */
442 449 : schedule_alarm(now);
443 449 : }
444 :
445 : /*
446 : * Enable the specified timeout to fire at the specified time.
447 : *
448 : * This is provided to support cases where there's a reason to calculate
449 : * the timeout by reference to some point other than "now". If there isn't,
450 : * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
451 : */
452 : void
453 0 : enable_timeout_at(TimeoutId id, TimestampTz fin_time)
454 : {
455 : TimestampTz now;
456 :
457 : /* Disable timeout interrupts for safety. */
458 0 : disable_alarm();
459 :
460 : /* Queue the timeout at the appropriate time. */
461 0 : now = GetCurrentTimestamp();
462 0 : enable_timeout(id, now, fin_time);
463 :
464 : /* Set the timer interrupt. */
465 0 : schedule_alarm(now);
466 0 : }
467 :
468 : /*
469 : * Enable multiple timeouts at once.
470 : *
471 : * This works like calling enable_timeout_after() and/or enable_timeout_at()
472 : * multiple times. Use this to reduce the number of GetCurrentTimestamp()
473 : * and setitimer() calls needed to establish multiple timeouts.
474 : */
475 : void
476 0 : enable_timeouts(const EnableTimeoutParams *timeouts, int count)
477 : {
478 : TimestampTz now;
479 : int i;
480 :
481 : /* Disable timeout interrupts for safety. */
482 0 : disable_alarm();
483 :
484 : /* Queue the timeout(s) at the appropriate times. */
485 0 : now = GetCurrentTimestamp();
486 :
487 0 : for (i = 0; i < count; i++)
488 : {
489 0 : TimeoutId id = timeouts[i].id;
490 : TimestampTz fin_time;
491 :
492 0 : switch (timeouts[i].type)
493 : {
494 : case TMPARAM_AFTER:
495 0 : fin_time = TimestampTzPlusMilliseconds(now,
496 : timeouts[i].delay_ms);
497 0 : enable_timeout(id, now, fin_time);
498 0 : break;
499 :
500 : case TMPARAM_AT:
501 0 : enable_timeout(id, now, timeouts[i].fin_time);
502 0 : break;
503 :
504 : default:
505 0 : elog(ERROR, "unrecognized timeout type %d",
506 : (int) timeouts[i].type);
507 : break;
508 : }
509 : }
510 :
511 : /* Set the timer interrupt. */
512 0 : schedule_alarm(now);
513 0 : }
514 :
515 : /*
516 : * Cancel the specified timeout.
517 : *
518 : * The timeout's I've-been-fired indicator is reset,
519 : * unless keep_indicator is true.
520 : *
521 : * When a timeout is canceled, any other active timeout remains in force.
522 : * It's not an error to disable a timeout that is not enabled.
523 : */
524 : void
525 51751 : disable_timeout(TimeoutId id, bool keep_indicator)
526 : {
527 : int i;
528 :
529 : /* Assert request is sane */
530 51751 : Assert(all_timeouts_initialized);
531 51751 : Assert(all_timeouts[id].timeout_handler != NULL);
532 :
533 : /* Disable timeout interrupts for safety. */
534 51751 : disable_alarm();
535 :
536 : /* Find the timeout and remove it from the active list. */
537 51751 : i = find_active_timeout(id);
538 51751 : if (i >= 0)
539 448 : remove_timeout_index(i);
540 :
541 : /* Mark it inactive, whether it was active or not. */
542 51751 : if (!keep_indicator)
543 51751 : all_timeouts[id].indicator = false;
544 :
545 : /* Reschedule the interrupt, if any timeouts remain active. */
546 51751 : if (num_active_timeouts > 0)
547 0 : schedule_alarm(GetCurrentTimestamp());
548 51751 : }
549 :
550 : /*
551 : * Cancel multiple timeouts at once.
552 : *
553 : * The timeouts' I've-been-fired indicators are reset,
554 : * unless timeouts[i].keep_indicator is true.
555 : *
556 : * This works like calling disable_timeout() multiple times.
557 : * Use this to reduce the number of GetCurrentTimestamp()
558 : * and setitimer() calls needed to cancel multiple timeouts.
559 : */
560 : void
561 0 : disable_timeouts(const DisableTimeoutParams *timeouts, int count)
562 : {
563 : int i;
564 :
565 0 : Assert(all_timeouts_initialized);
566 :
567 : /* Disable timeout interrupts for safety. */
568 0 : disable_alarm();
569 :
570 : /* Cancel the timeout(s). */
571 0 : for (i = 0; i < count; i++)
572 : {
573 0 : TimeoutId id = timeouts[i].id;
574 : int idx;
575 :
576 0 : Assert(all_timeouts[id].timeout_handler != NULL);
577 :
578 0 : idx = find_active_timeout(id);
579 0 : if (idx >= 0)
580 0 : remove_timeout_index(idx);
581 :
582 0 : if (!timeouts[i].keep_indicator)
583 0 : all_timeouts[id].indicator = false;
584 : }
585 :
586 : /* Reschedule the interrupt, if any timeouts remain active. */
587 0 : if (num_active_timeouts > 0)
588 0 : schedule_alarm(GetCurrentTimestamp());
589 0 : }
590 :
591 : /*
592 : * Disable SIGALRM and remove all timeouts from the active list,
593 : * and optionally reset their timeout indicators.
594 : */
595 : void
596 3220 : disable_all_timeouts(bool keep_indicators)
597 : {
598 3220 : disable_alarm();
599 :
600 : /*
601 : * Only bother to reset the timer if we think it's active. We could just
602 : * let the interrupt happen anyway, but it's probably a bit cheaper to do
603 : * setitimer() than to let the useless interrupt happen.
604 : */
605 3220 : if (num_active_timeouts > 0)
606 : {
607 : struct itimerval timeval;
608 :
609 0 : MemSet(&timeval, 0, sizeof(struct itimerval));
610 0 : if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
611 0 : elog(FATAL, "could not disable SIGALRM timer: %m");
612 : }
613 :
614 3220 : num_active_timeouts = 0;
615 :
616 3220 : if (!keep_indicators)
617 : {
618 : int i;
619 :
620 54740 : for (i = 0; i < MAX_TIMEOUTS; i++)
621 51520 : all_timeouts[i].indicator = false;
622 : }
623 3220 : }
624 :
625 : /*
626 : * Return the timeout's I've-been-fired indicator
627 : *
628 : * If reset_indicator is true, reset the indicator when returning true.
629 : * To avoid missing timeouts due to race conditions, we are careful not to
630 : * reset the indicator when returning false.
631 : */
632 : bool
633 2 : get_timeout_indicator(TimeoutId id, bool reset_indicator)
634 : {
635 2 : if (all_timeouts[id].indicator)
636 : {
637 1 : if (reset_indicator)
638 1 : all_timeouts[id].indicator = false;
639 1 : return true;
640 : }
641 1 : return false;
642 : }
643 :
644 : /*
645 : * Return the time when the timeout was most recently activated
646 : *
647 : * Note: will return 0 if timeout has never been activated in this process.
648 : * However, we do *not* reset the start_time when a timeout occurs, so as
649 : * not to create a race condition if SIGALRM fires just as some code is
650 : * about to fetch the value.
651 : */
652 : TimestampTz
653 0 : get_timeout_start_time(TimeoutId id)
654 : {
655 0 : return all_timeouts[id].start_time;
656 : }
657 :
658 : /*
659 : * Return the time when the timeout is, or most recently was, due to fire
660 : *
661 : * Note: will return 0 if timeout has never been activated in this process.
662 : * However, we do *not* reset the fin_time when a timeout occurs, so as
663 : * not to create a race condition if SIGALRM fires just as some code is
664 : * about to fetch the value.
665 : */
666 : TimestampTz
667 0 : get_timeout_finish_time(TimeoutId id)
668 : {
669 0 : return all_timeouts[id].fin_time;
670 : }
|