1 /*-------------------------------------------------------------------------
2  *
3  * timeout.c
4  *	  Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
5  *
6  * Portions Copyright (c) 1996-2016, 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
find_active_timeout(TimeoutId id)82 find_active_timeout(TimeoutId id)
83 {
84 	int			i;
85 
86 	for (i = 0; i < num_active_timeouts; i++)
87 	{
88 		if (active_timeouts[i]->index == id)
89 			return i;
90 	}
91 
92 	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
insert_timeout(TimeoutId id,int index)100 insert_timeout(TimeoutId id, int index)
101 {
102 	int			i;
103 
104 	if (index < 0 || index > num_active_timeouts)
105 		elog(FATAL, "timeout index %d out of range 0..%d", index,
106 			 num_active_timeouts);
107 
108 	for (i = num_active_timeouts - 1; i >= index; i--)
109 		active_timeouts[i + 1] = active_timeouts[i];
110 
111 	active_timeouts[index] = &all_timeouts[id];
112 
113 	num_active_timeouts++;
114 }
115 
116 /*
117  * Remove the index'th element from the timeout list.
118  */
119 static void
remove_timeout_index(int index)120 remove_timeout_index(int index)
121 {
122 	int			i;
123 
124 	if (index < 0 || index >= num_active_timeouts)
125 		elog(FATAL, "timeout index %d out of range 0..%d", index,
126 			 num_active_timeouts - 1);
127 
128 	for (i = index + 1; i < num_active_timeouts; i++)
129 		active_timeouts[i - 1] = active_timeouts[i];
130 
131 	num_active_timeouts--;
132 }
133 
134 /*
135  * Enable the specified timeout reason
136  */
137 static void
enable_timeout(TimeoutId id,TimestampTz now,TimestampTz fin_time)138 enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
139 {
140 	int			i;
141 
142 	/* Assert request is sane */
143 	Assert(all_timeouts_initialized);
144 	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 	i = find_active_timeout(id);
151 	if (i >= 0)
152 		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 	for (i = 0; i < num_active_timeouts; i++)
159 	{
160 		timeout_params *old_timeout = active_timeouts[i];
161 
162 		if (fin_time < old_timeout->fin_time)
163 			break;
164 		if (fin_time == old_timeout->fin_time && id < old_timeout->index)
165 			break;
166 	}
167 
168 	/*
169 	 * Mark the timeout active, and insert it into the active list.
170 	 */
171 	all_timeouts[id].indicator = false;
172 	all_timeouts[id].start_time = now;
173 	all_timeouts[id].fin_time = fin_time;
174 
175 	insert_timeout(id, i);
176 }
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
schedule_alarm(TimestampTz now)185 schedule_alarm(TimestampTz now)
186 {
187 	if (num_active_timeouts > 0)
188 	{
189 		struct itimerval timeval;
190 		long		secs;
191 		int			usecs;
192 
193 		MemSet(&timeval, 0, sizeof(struct itimerval));
194 
195 		/* Get the time remaining till the nearest pending timeout */
196 		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 		if (secs == 0 && usecs == 0)
204 			usecs = 1;
205 
206 		timeval.it_value.tv_sec = secs;
207 		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 		enable_alarm();
240 
241 		/* Set the alarm timer */
242 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
243 			elog(FATAL, "could not enable SIGALRM timer: %m");
244 	}
245 }
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
handle_sig_alarm(SIGNAL_ARGS)259 handle_sig_alarm(SIGNAL_ARGS)
260 {
261 	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 	HOLD_INTERRUPTS();
269 
270 	/*
271 	 * SIGALRM is always cause for waking anything waiting on the process
272 	 * latch.
273 	 */
274 	SetLatch(MyLatch);
275 
276 	/*
277 	 * Fire any pending timeouts, but only if we're enabled to do so.
278 	 */
279 	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 		disable_alarm();
287 
288 		if (num_active_timeouts > 0)
289 		{
290 			TimestampTz now = GetCurrentTimestamp();
291 
292 			/* While the first pending timeout has been reached ... */
293 			while (num_active_timeouts > 0 &&
294 				   now >= active_timeouts[0]->fin_time)
295 			{
296 				timeout_params *this_timeout = active_timeouts[0];
297 
298 				/* Remove it from the active list */
299 				remove_timeout_index(0);
300 
301 				/* Mark it as fired */
302 				this_timeout->indicator = true;
303 
304 				/* And call its handler function */
305 				(*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 				now = GetCurrentTimestamp();
313 			}
314 
315 			/* Done firing timeouts, so reschedule next interrupt if any */
316 			schedule_alarm(now);
317 		}
318 	}
319 
320 	RESUME_INTERRUPTS();
321 
322 	errno = save_errno;
323 }
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
InitializeTimeouts(void)340 InitializeTimeouts(void)
341 {
342 	int			i;
343 
344 	/* Initialize, or re-initialize, all local state */
345 	disable_alarm();
346 
347 	num_active_timeouts = 0;
348 
349 	for (i = 0; i < MAX_TIMEOUTS; i++)
350 	{
351 		all_timeouts[i].index = i;
352 		all_timeouts[i].indicator = false;
353 		all_timeouts[i].timeout_handler = NULL;
354 		all_timeouts[i].start_time = 0;
355 		all_timeouts[i].fin_time = 0;
356 	}
357 
358 	all_timeouts_initialized = true;
359 
360 	/* Now establish the signal handler */
361 	pqsignal(SIGALRM, handle_sig_alarm);
362 }
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
RegisterTimeout(TimeoutId id,timeout_handler_proc handler)373 RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
374 {
375 	Assert(all_timeouts_initialized);
376 
377 	/* There's no need to disable the signal handler here. */
378 
379 	if (id >= USER_TIMEOUT)
380 	{
381 		/* Allocate a user-defined timeout reason */
382 		for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
383 			if (all_timeouts[id].timeout_handler == NULL)
384 				break;
385 		if (id >= MAX_TIMEOUTS)
386 			ereport(FATAL,
387 					(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
388 					 errmsg("cannot add more timeout reasons")));
389 	}
390 
391 	Assert(all_timeouts[id].timeout_handler == NULL);
392 
393 	all_timeouts[id].timeout_handler = handler;
394 
395 	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
reschedule_timeouts(void)408 reschedule_timeouts(void)
409 {
410 	/* For flexibility, allow this to be called before we're initialized. */
411 	if (!all_timeouts_initialized)
412 		return;
413 
414 	/* Disable timeout interrupts for safety. */
415 	disable_alarm();
416 
417 	/* Reschedule the interrupt, if any timeouts remain active. */
418 	if (num_active_timeouts > 0)
419 		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
enable_timeout_after(TimeoutId id,int delay_ms)428 enable_timeout_after(TimeoutId id, int delay_ms)
429 {
430 	TimestampTz now;
431 	TimestampTz fin_time;
432 
433 	/* Disable timeout interrupts for safety. */
434 	disable_alarm();
435 
436 	/* Queue the timeout at the appropriate time. */
437 	now = GetCurrentTimestamp();
438 	fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
439 	enable_timeout(id, now, fin_time);
440 
441 	/* Set the timer interrupt. */
442 	schedule_alarm(now);
443 }
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
enable_timeout_at(TimeoutId id,TimestampTz fin_time)453 enable_timeout_at(TimeoutId id, TimestampTz fin_time)
454 {
455 	TimestampTz now;
456 
457 	/* Disable timeout interrupts for safety. */
458 	disable_alarm();
459 
460 	/* Queue the timeout at the appropriate time. */
461 	now = GetCurrentTimestamp();
462 	enable_timeout(id, now, fin_time);
463 
464 	/* Set the timer interrupt. */
465 	schedule_alarm(now);
466 }
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
enable_timeouts(const EnableTimeoutParams * timeouts,int count)476 enable_timeouts(const EnableTimeoutParams *timeouts, int count)
477 {
478 	TimestampTz now;
479 	int			i;
480 
481 	/* Disable timeout interrupts for safety. */
482 	disable_alarm();
483 
484 	/* Queue the timeout(s) at the appropriate times. */
485 	now = GetCurrentTimestamp();
486 
487 	for (i = 0; i < count; i++)
488 	{
489 		TimeoutId	id = timeouts[i].id;
490 		TimestampTz fin_time;
491 
492 		switch (timeouts[i].type)
493 		{
494 			case TMPARAM_AFTER:
495 				fin_time = TimestampTzPlusMilliseconds(now,
496 													   timeouts[i].delay_ms);
497 				enable_timeout(id, now, fin_time);
498 				break;
499 
500 			case TMPARAM_AT:
501 				enable_timeout(id, now, timeouts[i].fin_time);
502 				break;
503 
504 			default:
505 				elog(ERROR, "unrecognized timeout type %d",
506 					 (int) timeouts[i].type);
507 				break;
508 		}
509 	}
510 
511 	/* Set the timer interrupt. */
512 	schedule_alarm(now);
513 }
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
disable_timeout(TimeoutId id,bool keep_indicator)525 disable_timeout(TimeoutId id, bool keep_indicator)
526 {
527 	int			i;
528 
529 	/* Assert request is sane */
530 	Assert(all_timeouts_initialized);
531 	Assert(all_timeouts[id].timeout_handler != NULL);
532 
533 	/* Disable timeout interrupts for safety. */
534 	disable_alarm();
535 
536 	/* Find the timeout and remove it from the active list. */
537 	i = find_active_timeout(id);
538 	if (i >= 0)
539 		remove_timeout_index(i);
540 
541 	/* Mark it inactive, whether it was active or not. */
542 	if (!keep_indicator)
543 		all_timeouts[id].indicator = false;
544 
545 	/* Reschedule the interrupt, if any timeouts remain active. */
546 	if (num_active_timeouts > 0)
547 		schedule_alarm(GetCurrentTimestamp());
548 }
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
disable_timeouts(const DisableTimeoutParams * timeouts,int count)561 disable_timeouts(const DisableTimeoutParams *timeouts, int count)
562 {
563 	int			i;
564 
565 	Assert(all_timeouts_initialized);
566 
567 	/* Disable timeout interrupts for safety. */
568 	disable_alarm();
569 
570 	/* Cancel the timeout(s). */
571 	for (i = 0; i < count; i++)
572 	{
573 		TimeoutId	id = timeouts[i].id;
574 		int			idx;
575 
576 		Assert(all_timeouts[id].timeout_handler != NULL);
577 
578 		idx = find_active_timeout(id);
579 		if (idx >= 0)
580 			remove_timeout_index(idx);
581 
582 		if (!timeouts[i].keep_indicator)
583 			all_timeouts[id].indicator = false;
584 	}
585 
586 	/* Reschedule the interrupt, if any timeouts remain active. */
587 	if (num_active_timeouts > 0)
588 		schedule_alarm(GetCurrentTimestamp());
589 }
590 
591 /*
592  * Disable SIGALRM and remove all timeouts from the active list,
593  * and optionally reset their timeout indicators.
594  */
595 void
disable_all_timeouts(bool keep_indicators)596 disable_all_timeouts(bool keep_indicators)
597 {
598 	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 	if (num_active_timeouts > 0)
606 	{
607 		struct itimerval timeval;
608 
609 		MemSet(&timeval, 0, sizeof(struct itimerval));
610 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
611 			elog(FATAL, "could not disable SIGALRM timer: %m");
612 	}
613 
614 	num_active_timeouts = 0;
615 
616 	if (!keep_indicators)
617 	{
618 		int			i;
619 
620 		for (i = 0; i < MAX_TIMEOUTS; i++)
621 			all_timeouts[i].indicator = false;
622 	}
623 }
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
get_timeout_indicator(TimeoutId id,bool reset_indicator)633 get_timeout_indicator(TimeoutId id, bool reset_indicator)
634 {
635 	if (all_timeouts[id].indicator)
636 	{
637 		if (reset_indicator)
638 			all_timeouts[id].indicator = false;
639 		return true;
640 	}
641 	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
get_timeout_start_time(TimeoutId id)653 get_timeout_start_time(TimeoutId id)
654 {
655 	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
get_timeout_finish_time(TimeoutId id)667 get_timeout_finish_time(TimeoutId id)
668 {
669 	return all_timeouts[id].fin_time;
670 }
671