/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 | } |