/* * EnableCatchupInterrupt * * This is called by the PostgresMain main loop just before waiting * for a frontend command. We process any pending catchup events, * and enable the signal handler to process future events directly. * * NOTE: the signal handler starts out disabled, and stays so until * PostgresMain calls this the first time. */ void EnableCatchupInterrupt(void) { /* * This code is tricky because we are communicating with a signal handler * that could interrupt us at any point. If we just checked * catchupInterruptOccurred and then set catchupInterruptEnabled, we could * fail to respond promptly to a signal that happens in between those two * steps. (A very small time window, perhaps, but Murphy's Law says you * can hit it...) Instead, we first set the enable flag, then test the * occurred flag. If we see an unserviced interrupt has occurred, we * re-clear the enable flag before going off to do the service work. (That * prevents re-entrant invocation of ProcessCatchupEvent() if another * interrupt occurs.) If an interrupt comes in between the setting and * clearing of catchupInterruptEnabled, then it will have done the service * work and left catchupInterruptOccurred zero, so we have to check again * after clearing enable. The whole thing has to be in a loop in case * another interrupt occurs while we're servicing the first. Once we get * out of the loop, enable is set and we know there is no unserviced * interrupt. * * NB: an overenthusiastic optimizing compiler could easily break this * code. Hopefully, they all understand what "volatile" means these days. */ for (;;) { catchupInterruptEnabled = 1; if (!catchupInterruptOccurred) break; catchupInterruptEnabled = 0; if (catchupInterruptOccurred) ProcessCatchupEvent(); } }
/* * HandleCatchupInterrupt * * This is called when PROCSIG_CATCHUP_INTERRUPT is received. * * If we are idle (catchupInterruptEnabled is set), we can safely * invoke ProcessCatchupEvent directly. Otherwise, just set a flag * to do it later. (Note that it's quite possible for normal processing * of the current transaction to cause ReceiveSharedInvalidMessages() * to be run later on; in that case the flag will get cleared again, * since there's no longer any reason to do anything.) */ void HandleCatchupInterrupt(void) { /* * Note: this is called by a SIGNAL HANDLER. You must be very wary what * you do here. */ /* Don't joggle the elbow of proc_exit */ if (proc_exit_inprogress) return; if (catchupInterruptEnabled) { bool save_ImmediateInterruptOK = ImmediateInterruptOK; /* * We may be called while ImmediateInterruptOK is true; turn it off * while messing with the catchup state. (We would have to save and * restore it anyway, because PGSemaphore operations inside * ProcessCatchupEvent() might reset it.) */ ImmediateInterruptOK = false; /* * I'm not sure whether some flavors of Unix might allow another * SIGUSR1 occurrence to recursively interrupt this routine. To cope * with the possibility, we do the same sort of dance that * EnableCatchupInterrupt must do --- see that routine for comments. */ catchupInterruptEnabled = 0; /* disable any recursive signal */ catchupInterruptOccurred = 1; /* do at least one iteration */ for (;;) { catchupInterruptEnabled = 1; if (!catchupInterruptOccurred) break; catchupInterruptEnabled = 0; if (catchupInterruptOccurred) { /* Here, it is finally safe to do stuff. */ ProcessCatchupEvent(); } } /* * Restore ImmediateInterruptOK, and check for interrupts if needed. */ ImmediateInterruptOK = save_ImmediateInterruptOK; if (save_ImmediateInterruptOK) CHECK_FOR_INTERRUPTS(); } else { /* * In this path it is NOT SAFE to do much of anything, except this: */ catchupInterruptOccurred = 1; } }