|
16 | 16 |
|
17 | 17 | #include <sys/time.h>
|
18 | 18 |
|
| 19 | +#include "miscadmin.h" |
19 | 20 | #include "storage/proc.h"
|
20 | 21 | #include "utils/timeout.h"
|
21 | 22 | #include "utils/timestamp.h"
|
@@ -259,6 +260,23 @@ handle_sig_alarm(SIGNAL_ARGS)
|
259 | 260 | {
|
260 | 261 | int save_errno = errno;
|
261 | 262 |
|
| 263 | + /* |
| 264 | + * We may be executing while ImmediateInterruptOK is true (e.g., when |
| 265 | + * mainline is waiting for a lock). If SIGINT or similar arrives while |
| 266 | + * this code is running, we'd lose control and perhaps leave our data |
| 267 | + * structures in an inconsistent state. Hold off interrupts to prevent |
| 268 | + * that. |
| 269 | + * |
| 270 | + * Note: it's possible for a SIGINT to interrupt handle_sig_alarm even |
| 271 | + * before we reach HOLD_INTERRUPTS(); the net effect would be as if the |
| 272 | + * SIGALRM event had been silently lost. Therefore error recovery must |
| 273 | + * include some action that will allow any lost interrupt to be |
| 274 | + * rescheduled. Disabling some or all timeouts is sufficient, or if |
| 275 | + * that's not appropriate, reschedule_timeouts() can be called. Also, the |
| 276 | + * signal blocking hazard described below applies here too. |
| 277 | + */ |
| 278 | + HOLD_INTERRUPTS(); |
| 279 | + |
262 | 280 | /*
|
263 | 281 | * SIGALRM is always cause for waking anything waiting on the process
|
264 | 282 | * latch. Cope with MyProc not being there, as the startup process also
|
@@ -311,6 +329,20 @@ handle_sig_alarm(SIGNAL_ARGS)
|
311 | 329 | }
|
312 | 330 | }
|
313 | 331 |
|
| 332 | + /* |
| 333 | + * Re-allow query cancel, and then service any cancel request that arrived |
| 334 | + * meanwhile (this might in particular include a cancel request fired by |
| 335 | + * one of the timeout handlers). |
| 336 | + * |
| 337 | + * Note: a longjmp from here is safe so far as our own data structures are |
| 338 | + * concerned; but on platforms that block a signal before calling the |
| 339 | + * handler and then un-block it on return, longjmping out of the signal |
| 340 | + * handler leaves SIGALRM still blocked. Error cleanup is responsible for |
| 341 | + * unblocking any blocked signals. |
| 342 | + */ |
| 343 | + RESUME_INTERRUPTS(); |
| 344 | + CHECK_FOR_INTERRUPTS(); |
| 345 | + |
314 | 346 | errno = save_errno;
|
315 | 347 | }
|
316 | 348 |
|
@@ -387,6 +419,30 @@ RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
|
387 | 419 | return id;
|
388 | 420 | }
|
389 | 421 |
|
| 422 | +/* |
| 423 | + * Reschedule any pending SIGALRM interrupt. |
| 424 | + * |
| 425 | + * This can be used during error recovery in case query cancel resulted in loss |
| 426 | + * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it |
| 427 | + * could do anything). But note it's not necessary if any of the public |
| 428 | + * enable_ or disable_timeout functions are called in the same area, since |
| 429 | + * those all do schedule_alarm() internally if needed. |
| 430 | + */ |
| 431 | +void |
| 432 | +reschedule_timeouts(void) |
| 433 | +{ |
| 434 | + /* For flexibility, allow this to be called before we're initialized. */ |
| 435 | + if (!all_timeouts_initialized) |
| 436 | + return; |
| 437 | + |
| 438 | + /* Disable timeout interrupts for safety. */ |
| 439 | + disable_alarm(); |
| 440 | + |
| 441 | + /* Reschedule the interrupt, if any timeouts remain active. */ |
| 442 | + if (num_active_timeouts > 0) |
| 443 | + schedule_alarm(GetCurrentTimestamp()); |
| 444 | +} |
| 445 | + |
390 | 446 | /*
|
391 | 447 | * Enable the specified timeout to fire after the specified delay.
|
392 | 448 | *
|
|
0 commit comments