FlushProcessWriteBuffers();
typedef void (*PAL_ActivationFunction)(CONTEXT *context);
-typedef BOOL (*PAL_SafeActivationCheckFunction)(SIZE_T ip);
+typedef BOOL (*PAL_SafeActivationCheckFunction)(SIZE_T ip, BOOL checkingCurrentThread);
PALIMPORT
VOID
&count);
_ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_THREAD_STATE64\n");
- if ((g_safeActivationCheckFunction != NULL) && g_safeActivationCheckFunction(ThreadState.__rip))
+ if ((g_safeActivationCheckFunction != NULL) && g_safeActivationCheckFunction(ThreadState.__rip, /* checkingCurrentThread */ FALSE))
{
// TODO: it would be nice to preserve the red zone in case a jitter would want to use it
// Do we really care about unwinding through the wrapper?
&winContext,
CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
- if (g_safeActivationCheckFunction(CONTEXTGetPC(&winContext)))
+ if (g_safeActivationCheckFunction(CONTEXTGetPC(&winContext), /* checkingCurrentThread */ TRUE))
{
g_activationFunction(&winContext);
}
BOOL PALAPI IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord)
{
+ Thread *pThread = GetThread();
PCODE controlPc = GetIP(contextRecord);
+ // It is safe to call the ExecutionManager::IsManagedCode only if the current thread is in
+ // the cooperative mode. Otherwise ExecutionManager::IsManagedCode could deadlock if
+ // the exception happened when the thread was holding the ExecutionManager's writer lock.
+ // When the thread is in preemptive mode, we know for sure that it is not executing managed code.
+ BOOL isManagedCode = (pThread != NULL && pThread->PreemptiveGCDisabled() && ExecutionManager::IsManagedCode(controlPc));
return g_fEEStarted && (
exceptionRecord->ExceptionCode == STATUS_BREAKPOINT ||
exceptionRecord->ExceptionCode == STATUS_SINGLE_STEP ||
- ExecutionManager::IsManagedCode(controlPc) ||
+ isManagedCode ||
IsIPInMarkedJitHelper(controlPc));
}
{
// A hardware exception is handled only if it happened in a jitted code or
// in one of the JIT helper functions (JIT_MemSet, ...)
+ Thread *pThread = GetThread();
PCODE controlPc = GetIP(&ex->ContextRecord);
- if (ExecutionManager::IsManagedCode(controlPc) && IsGcMarker(ex->ExceptionRecord.ExceptionCode, &ex->ContextRecord))
+ BOOL isManagedCode = (pThread != NULL && pThread->PreemptiveGCDisabled() && ExecutionManager::IsManagedCode(controlPc));
+ if (isManagedCode && IsGcMarker(ex->ExceptionRecord.ExceptionCode, &ex->ContextRecord))
{
RtlRestoreContext(&ex->ContextRecord, &ex->ExceptionRecord);
UNREACHABLE();
// This function is called by PAL to check if the specified instruction pointer
// is in a function where we can safely inject activation.
-BOOL PALAPI CheckActivationSafePoint(SIZE_T ip)
+BOOL PALAPI CheckActivationSafePoint(SIZE_T ip, BOOL checkingCurrentThread)
{
- return ExecutionManager::IsManagedCode(ip);
+ Thread *pThread = GetThread();
+ // It is safe to call the ExecutionManager::IsManagedCode only if we are making the check for
+ // a thread different from the current one or if the current thread is in the cooperative mode.
+ // Otherwise ExecutionManager::IsManagedCode could deadlock if the activation happened when the
+ // thread was holding the ExecutionManager's writer lock.
+ // When the thread is in preemptive mode, we know for sure that it is not executing managed code.
+ BOOL checkForManagedCode = !checkingCurrentThread || (pThread != NULL && pThread->PreemptiveGCDisabled());
+ return checkForManagedCode && ExecutionManager::IsManagedCode(ip);
}
// This function is called when a GC is pending. It tries to ensure that the current
//
// - If the thread is in interruptible managed code, we will push a frame that
// has information about the context that was interrupted and then switch to
-// premptive GC mode so that the pending GC can proceed, and then switch back.
+// preemptive GC mode so that the pending GC can proceed, and then switch back.
//
// - If the thread is in uninterruptible managed code, we will patch the return
// address to take the thread to the appropriate stub (based on the return
// This function can only be called when the interrupted thread is in
// an activation safe point.
- _ASSERTE(CheckActivationSafePoint(ip));
+ _ASSERTE(CheckActivationSafePoint(ip, /* checkingCurrentThread */ TRUE));
Thread::WorkingOnThreadContextHolder workingOnThreadContext(pThread);
if (!workingOnThreadContext.Acquired())