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