/* * 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()); }
/* * Enable the specified timeout reason */ static void enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time) { int i; /* Assert request is sane */ Assert(all_timeouts_initialized); Assert(all_timeouts[id].timeout_handler != NULL); /* * If this timeout was already active, momentarily disable it. We * interpret the call as a directive to reschedule the timeout. */ i = find_active_timeout(id); if (i >= 0) remove_timeout_index(i); /* * Find out the index where to insert the new timeout. We sort by * fin_time, and for equal fin_time by priority. */ for (i = 0; i < num_active_timeouts; i++) { timeout_params *old_timeout = active_timeouts[i]; if (fin_time < old_timeout->fin_time) break; if (fin_time == old_timeout->fin_time && id < old_timeout->index) break; } /* * Mark the timeout active, and insert it into the active list. */ all_timeouts[id].indicator = false; all_timeouts[id].start_time = now; all_timeouts[id].fin_time = fin_time; insert_timeout(id, i); }
/* * 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; }