/* * Checks if RedZoneHandler_IsVmemRedZone() properly identifies red zone */ void test__RedZoneHandler_IsVmemRedZone__ProperlyIdentifiesRedZone(void **state) { vmemTrackerInited = false; /* No red zone detection if vmem tracker is not initialized */ assert_false(RedZoneHandler_IsVmemRedZone()); vmemTrackerInited = true; static int32 fakeSegmentVmemChunks = 0; segmentVmemChunks = &fakeSegmentVmemChunks; redZoneChunks = INT32_MAX; *segmentVmemChunks = INT32_MAX; /* Both segment vmem and red zone is INT32_MAX. It's not a red-zone */ assert_false(RedZoneHandler_IsVmemRedZone()); /* 100 chunks */ *segmentVmemChunks = 100; redZoneChunks = 80; /* segmentVmemChunks exceeds redZoneChunks. So, should be red zone */ assert_true(RedZoneHandler_IsVmemRedZone()); vmemTrackerInited = false; /* * segmentVmemChunks exceeds redZoneChunks. But vmem tracker is not * initialized. Therefore, no red zone detection */ assert_false(RedZoneHandler_IsVmemRedZone()); }
/* * In a red-zone this method identifies the top vmem consuming session, * and requests it to cleanup. If the red-zone handler determines itself * as the runaway session, it also starts the cleanup. */ void RedZoneHandler_DetectRunawaySession() { /* * InterruptHoldoffCount > 0 indicates we are in a sensitive code path that doesn't * like a control flow disruption as may happen from a pending die/cancel interrupt. * As we may eventually ERROR out from this method (during RunawayCleaner_StartCleanup) * we want to make sure that HOLD_INTERRUPTS() was not called (i.e., InterruptHoldoffCount == 0). * * What happens if we don't check for InterruptHoldoffCount? One example is LWLockAcquire() * which calls HOLD_INTERRUPTS() to ensure that no unexpected control * flow disruption happens because of FATAL/ERROR as done from die/cancel interrupt * handler. If we ignore InterruptHoldoffCount, the PGSemaphoreLock() (called from LWLockAcquire) * would call CHECK_FOR_INTERRUPTS() and we may throw ERROR if the current session is a runaway. * Unfortunately, LWLockAcquire shares the semaphore with the regular lock manager and * ProcWaitForSignal. Therefore, LWLockAcquire may wake up multiple times during its wait * for a semaphore which may not relate to an actual LWLock release. This requires LWLockAcquire * to keep track of how many of those false wake events it has consumed (by decrementing semaphore * when it shouldn't have done so) and LWLockAcquire rollback the semaphore decrements for * the irrelevant wake up events by re-incrementing once it actually acquires the lock. * Therefore, an unexpected control flow out of the LWLockAcquire before it properly rolled back * may prevent the LWLockAcquire to rollback the false wake events. Although we do call LWLockRelease * during an error handling, that doesn't guarantee that the falsely consumed semaphore wake * events would be rolled back (i.e., semaphore does not get re-incremented during error handling) as * done at the end of LWLockAcquire. This may cause the semaphore to never wake up other waiting * processes and therefore may cause other processes to hang perpetually. */ if (!RedZoneHandler_IsVmemRedZone() || InterruptHoldoffCount > 0 || CritSectionCount > 0) { return; } /* We don't support runaway detection/termination from non-owner thread */ Assert(MemoryProtection_IsOwnerThread()); Assert(gp_mp_inited); RedZoneHandler_FlagTopConsumer(); RunawayCleaner_StartCleanup(); }