Coverage Report

Created: 2025-08-12 06:43

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