/* * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro * * If an interrupt condition is pending, and it's safe to service it, * then clear the flag and accept the interrupt. Called only when * InterruptPending is true. */ voidProcessInterrupts(void) { /* OK to accept interrupt now? */ if (t_thrd.int_cxt.InterruptHoldoffCount != 0 || t_thrd.int_cxt.CritSectionCount != 0) return;
// The 'u_sess->stream_cxt.in_waiting_quit' flag is set to true to enable signal handling when waiting sub stream // threads quit. At the same time, if we get a SIGTERM signal, this signal should be held and the 'InterruptPending' // flag should not be set to false immediately. After all sub thread quit and the top consumer goes back to // ReadCommand again, the pending interrupt can be safely handled in function prepare_for_client_read. // if (t_thrd.int_cxt.ProcDiePending && u_sess->stream_cxt.in_waiting_quit) { // It's more efficient to notify all stream threads to cancel the query first // and then top consumer can quit quickly. // StreamNodeGroup::cancelStreamThread(); return; }
if (StreamThreadAmI() && u_sess->debug_query_id == 0) { Assert(0); }
InterruptPending = false; if (t_thrd.wlm_cxt.wlmalarm_pending) {...} if (t_thrd.int_cxt.ProcDiePending && !u_sess->stream_cxt.in_waiting_quit) {...} if (t_thrd.int_cxt.ClientConnectionLost && !u_sess->stream_cxt.in_waiting_quit) {...} if (t_thrd.int_cxt.QueryCancelPending) {...}
这里列出部分代码,从注释中可以看到,Called only when InterruptPending is true.。那么我们要去看这个InterruptPending是怎么一回事。
/* * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro * * If an interrupt condition is pending, and it's safe to service it, * then clear the flag and accept the interrupt. Called only when * InterruptPending is true. */ voidProcessInterrupts(void) { /* OK to accept interrupt now? */ if (t_thrd.int_cxt.InterruptHoldoffCount != 0 || t_thrd.int_cxt.CritSectionCount != 0) return; if (t_thrd.bn && ((unsignedint)(t_thrd.bn->flag) & THRD_SIGTERM)) { t_thrd.int_cxt.ProcDiePending = true; t_thrd.bn->flag = ((unsignedint)(t_thrd.bn->flag)) & ~THRD_SIGTERM; }
// The 'u_sess->stream_cxt.in_waiting_quit' flag is set to true to enable signal handling when waiting sub stream // threads quit. At the same time, if we get a SIGTERM signal, this signal should be held and the 'InterruptPending' // flag should not be set to false immediately. After all sub thread quit and the top consumer goes back to // ReadCommand again, the pending interrupt can be safely handled in function prepare_for_client_read. // if (t_thrd.int_cxt.ProcDiePending && u_sess->stream_cxt.in_waiting_quit) { // It's more efficient to notify all stream threads to cancel the query first // and then top consumer can quit quickly. // StreamNodeGroup::cancelStreamThread(); return; }
if (StreamThreadAmI() && u_sess->debug_query_id == 0) { Assert(0); }
InterruptPending = false;
if (t_thrd.wlm_cxt.wlmalarm_pending) { t_thrd.wlm_cxt.wlmalarm_pending = false; (void)WLMProcessWorkloadManager(); }
if (t_thrd.int_cxt.ProcDiePending && !u_sess->stream_cxt.in_waiting_quit) { ... } if (t_thrd.int_cxt.ClientConnectionLost && !u_sess->stream_cxt.in_waiting_quit) { ... } if (t_thrd.int_cxt.QueryCancelPending) { ... } /* If we get here, do nothing (probably, t_thrd.int_cxt.QueryCancelPending was reset) */ }
/***************************************************************************** * System interrupt and critical section handling * * There are two types of interrupts that a running backend needs to accept * without messing up its state: QueryCancel (SIGINT) and ProcDie (SIGTERM). * In both cases, we need to be able to clean up the current transaction * gracefully, so we can't respond to the interrupt instantaneously --- * there's no guarantee that internal data structures would be self-consistent * if the code is interrupted at an arbitrary instant. Instead, the signal * handlers set flags that are checked periodically during execution. * * The CHECK_FOR_INTERRUPTS() macro is called at strategically located spots * where it is normally safe to accept a cancel or die interrupt. In some * cases, we invoke CHECK_FOR_INTERRUPTS() inside low-level subroutines that * might sometimes be called in contexts that do *not* want to allow a cancel * or die interrupt. The HOLD_INTERRUPTS() and RESUME_INTERRUPTS() macros * allow code to ensure that no cancel or die interrupt will be accepted, * even if CHECK_FOR_INTERRUPTS() gets called in a subroutine. The interrupt * will be held off until CHECK_FOR_INTERRUPTS() is done outside any * HOLD_INTERRUPTS() ... RESUME_INTERRUPTS() section. * * Special mechanisms are used to let an interrupt be accepted when we are * waiting for a lock or when we are waiting for command input (but, of * course, only if the interrupt holdoff counter is zero). See the * related code for details. * * A lost connection is handled similarly, although the loss of connection * does not raise a signal, but is detected when we fail to write to the * socket. If there was a signal for a broken connection, we could make use of * it by setting t_thrd.int_cxt.ClientConnectionLost in the signal handler. * * A related, but conceptually distinct, mechanism is the "critical section" * mechanism. A critical section not only holds off cancel/die interrupts, * but causes any ereport(ERROR) or ereport(FATAL) to become ereport(PANIC) * --- that is, a system-wide reset is forced. Needless to say, only really * *critical* code should be marked as a critical section! Currently, this * mechanism is only used for XLOG-related code. * *****************************************************************************/