/* * Cancel multiple timeouts at once. * * The timeouts' I've-been-fired indicators are reset, * unless timeouts[i].keep_indicator is true. * * This works like calling disable_timeout() multiple times. * Use this to reduce the number of GetCurrentTimestamp() * and setitimer() calls needed to cancel multiple timeouts. */ void disable_timeouts(const DisableTimeoutParams *timeouts, int count) { int i; Assert(all_timeouts_initialized); /* Disable timeout interrupts for safety. */ disable_alarm(); /* Cancel the timeout(s). */ for (i = 0; i < count; i++) { TimeoutId id = timeouts[i].id; int idx; Assert(all_timeouts[id].timeout_handler != NULL); idx = find_active_timeout(id); if (idx >= 0) remove_timeout_index(idx); if (!timeouts[i].keep_indicator) all_timeouts[id].indicator = false; } /* Reschedule the interrupt, if any timeouts remain active. */ if (num_active_timeouts > 0) schedule_alarm(GetCurrentTimestamp()); }
/* * Cancel the specified timeout. * * The timeout's I've-been-fired indicator is reset, * unless keep_indicator is true. * * When a timeout is canceled, any other active timeout remains in force. * It's not an error to disable a timeout that is not enabled. */ void disable_timeout(TimeoutId id, bool keep_indicator) { int i; /* Assert request is sane */ Assert(all_timeouts_initialized); Assert(all_timeouts[id].timeout_handler != NULL); /* Disable timeout interrupts for safety. */ disable_alarm(); /* Find the timeout and remove it from the active list. */ i = find_active_timeout(id); if (i >= 0) remove_timeout_index(i); /* Mark it inactive, whether it was active or not. */ if (!keep_indicator) all_timeouts[id].indicator = false; /* Reschedule the interrupt, if any timeouts remain active. */ if (num_active_timeouts > 0) schedule_alarm(GetCurrentTimestamp()); }
/* * Reschedule any pending SIGALRM interrupt. * * This can be used during error recovery in case query cancel resulted in loss * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it * could do anything). But note it's not necessary if any of the public * enable_ or disable_timeout functions are called in the same area, since * those all do schedule_alarm() internally if needed. */ void reschedule_timeouts(void) { /* For flexibility, allow this to be called before we're initialized. */ if (!all_timeouts_initialized) return; /* Disable timeout interrupts for safety. */ disable_alarm(); /* Reschedule the interrupt, if any timeouts remain active. */ if (num_active_timeouts > 0) schedule_alarm(GetCurrentTimestamp()); }
/* * Enable the specified timeout to fire at the specified time. * * This is provided to support cases where there's a reason to calculate * the timeout by reference to some point other than "now". If there isn't, * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice. */ void enable_timeout_at(TimeoutId id, TimestampTz fin_time) { TimestampTz now; /* Disable timeout interrupts for safety. */ disable_alarm(); /* Queue the timeout at the appropriate time. */ now = GetCurrentTimestamp(); enable_timeout(id, now, fin_time); /* Set the timer interrupt. */ schedule_alarm(now); }
/* * Enable the specified timeout to fire after the specified delay. * * Delay is given in milliseconds. */ void enable_timeout_after(TimeoutId id, int delay_ms) { TimestampTz now; TimestampTz fin_time; /* Disable timeout interrupts for safety. */ disable_alarm(); /* Queue the timeout at the appropriate time. */ now = GetCurrentTimestamp(); fin_time = TimestampTzPlusMilliseconds(now, delay_ms); enable_timeout(id, now, fin_time); /* Set the timer interrupt. */ schedule_alarm(now); }
/* * Enable multiple timeouts at once. * * This works like calling enable_timeout_after() and/or enable_timeout_at() * multiple times. Use this to reduce the number of GetCurrentTimestamp() * and setitimer() calls needed to establish multiple timeouts. */ void enable_timeouts(const EnableTimeoutParams *timeouts, int count) { TimestampTz now; int i; /* Disable timeout interrupts for safety. */ disable_alarm(); /* Queue the timeout(s) at the appropriate times. */ now = GetCurrentTimestamp(); for (i = 0; i < count; i++) { TimeoutId id = timeouts[i].id; TimestampTz fin_time; switch (timeouts[i].type) { case TMPARAM_AFTER: fin_time = TimestampTzPlusMilliseconds(now, timeouts[i].delay_ms); enable_timeout(id, now, fin_time); break; case TMPARAM_AT: enable_timeout(id, now, timeouts[i].fin_time); break; default: elog(ERROR, "unrecognized timeout type %d", (int) timeouts[i].type); break; } } /* Set the timer interrupt. */ schedule_alarm(now); }
/* * Signal handler for SIGALRM * * Process any active timeout reasons and then reschedule the interrupt * as needed. */ static void handle_sig_alarm(SIGNAL_ARGS) { int save_errno = errno; bool save_ImmediateInterruptOK = ImmediateInterruptOK; /* * We may be executing while ImmediateInterruptOK is true (e.g., when * mainline is waiting for a lock). If SIGINT or similar arrives while * this code is running, we'd lose control and perhaps leave our data * structures in an inconsistent state. Disable immediate interrupts, and * just to be real sure, bump the holdoff counter as well. (The reason * for this belt-and-suspenders-too approach is to make sure that nothing * bad happens if a timeout handler calls code that manipulates * ImmediateInterruptOK.) * * Note: it's possible for a SIGINT to interrupt handle_sig_alarm before * we manage to do this; the net effect would be as if the SIGALRM event * had been silently lost. Therefore error recovery must include some * action that will allow any lost interrupt to be rescheduled. Disabling * some or all timeouts is sufficient, or if that's not appropriate, * reschedule_timeouts() can be called. Also, the signal blocking hazard * described below applies here too. */ ImmediateInterruptOK = false; HOLD_INTERRUPTS(); /* * SIGALRM is always cause for waking anything waiting on the process * latch. Cope with MyProc not being there, as the startup process also * uses this signal handler. */ if (MyProc) SetLatch(&MyProc->procLatch); /* * Fire any pending timeouts, but only if we're enabled to do so. */ if (alarm_enabled) { /* * Disable alarms, just in case this platform allows signal handlers * to interrupt themselves. schedule_alarm() will re-enable if * appropriate. */ disable_alarm(); if (num_active_timeouts > 0) { TimestampTz now = GetCurrentTimestamp(); /* While the first pending timeout has been reached ... */ while (num_active_timeouts > 0 && now >= active_timeouts[0]->fin_time) { timeout_params *this_timeout = active_timeouts[0]; /* Remove it from the active list */ remove_timeout_index(0); /* Mark it as fired */ this_timeout->indicator = true; /* And call its handler function */ (*this_timeout->timeout_handler) (); /* * The handler might not take negligible time (CheckDeadLock * for instance isn't too cheap), so let's update our idea of * "now" after each one. */ now = GetCurrentTimestamp(); } /* Done firing timeouts, so reschedule next interrupt if any */ schedule_alarm(now); } } /* * Re-allow query cancel, and then try to service any cancel request that * arrived meanwhile (this might in particular include a cancel request * fired by one of the timeout handlers). Since we are in a signal * handler, we mustn't call ProcessInterrupts unless ImmediateInterruptOK * is set; if it isn't, the cancel will happen at the next mainline * CHECK_FOR_INTERRUPTS. * * Note: a longjmp from here is safe so far as our own data structures are * concerned; but on platforms that block a signal before calling the * handler and then un-block it on return, longjmping out of the signal * handler leaves SIGALRM still blocked. Error cleanup is responsible for * unblocking any blocked signals. */ RESUME_INTERRUPTS(); ImmediateInterruptOK = save_ImmediateInterruptOK; if (save_ImmediateInterruptOK) CHECK_FOR_INTERRUPTS(); errno = save_errno; }
/* * Signal handler for SIGALRM * * Process any active timeout reasons and then reschedule the interrupt * as needed. */ static void handle_sig_alarm(SIGNAL_ARGS) { int save_errno = errno; /* * Bump the holdoff counter, to make sure nothing we call will process * interrupts directly. No timeout handler should do that, but these * failures are hard to debug, so better be sure. */ HOLD_INTERRUPTS(); /* * SIGALRM is always cause for waking anything waiting on the process * latch. */ SetLatch(MyLatch); /* * Fire any pending timeouts, but only if we're enabled to do so. */ if (alarm_enabled) { /* * Disable alarms, just in case this platform allows signal handlers * to interrupt themselves. schedule_alarm() will re-enable if * appropriate. */ disable_alarm(); if (num_active_timeouts > 0) { TimestampTz now = GetCurrentTimestamp(); /* While the first pending timeout has been reached ... */ while (num_active_timeouts > 0 && now >= active_timeouts[0]->fin_time) { timeout_params *this_timeout = active_timeouts[0]; /* Remove it from the active list */ remove_timeout_index(0); /* Mark it as fired */ this_timeout->indicator = true; /* And call its handler function */ (*this_timeout->timeout_handler) (); /* * The handler might not take negligible time (CheckDeadLock * for instance isn't too cheap), so let's update our idea of * "now" after each one. */ now = GetCurrentTimestamp(); } /* Done firing timeouts, so reschedule next interrupt if any */ schedule_alarm(now); } } RESUME_INTERRUPTS(); errno = save_errno; }