Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,9 +900,9 @@ HRESULT ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThre
// which has the same dereference semantics as a real GC handle.
{
OBJECTHANDLE ohException = thread->GetThrowableAsPseudoHandle();
if (ohException == (OBJECTHANDLE)NULL)
if (ohException == (OBJECTHANDLE)NULL && !thread->IsLastThrownObjectNull())
{
ohException = thread->m_LastThrownObjectHandle;
ohException = thread->LastThrownObjectHandle();
}
threadData->lastThrownObjectHandle = TO_CDADDR(ohException);
}
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,15 +521,15 @@ ClrDataTask::GetLastExceptionState(

EX_TRY
{
if (m_thread->m_LastThrownObjectHandle)
if (!m_thread->IsLastThrownObjectNull())
{
*exception = new (nothrow)
ClrDataExceptionState(m_dac,
AppDomain::GetCurrentDomain(),
m_thread,
CLRDATA_EXCEPTION_PARTIAL,
NULL,
m_thread->m_LastThrownObjectHandle,
m_thread->LastThrownObjectHandle(),
NULL);
status = *exception ? S_OK : E_OUTOFMEMORY;
}
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/debug/ee/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7449,7 +7449,7 @@ HRESULT Debugger::SendException(Thread *pThread,
(fFirstChance && (!pExState->GetFlags()->SentDebugFirstChance() || !pExState->GetFlags()->SentDebugUserFirstChance())));

// There must be a managed exception object to send a managed exception event
if (g_pEEInterface->IsThreadExceptionNull(pThread) && (pThread->LastThrownObjectHandle() == NULL))
if (pThread->IsThrowableNull() && pThread->IsLastThrownObjectNull())
{
managedEventNeeded = FALSE;
}
Expand Down Expand Up @@ -7736,7 +7736,7 @@ LONG Debugger::NotifyOfCHFFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID p
{
CONTRACTL
{
if ((GetThreadNULLOk() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread()))
if ((GetThreadNULLOk() == NULL) || GetThread()->IsThrowableNull())
{
NOTHROW;
GC_NOTRIGGER;
Expand Down Expand Up @@ -7766,7 +7766,7 @@ LONG Debugger::NotifyOfCHFFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID p
// useful information for the debugger and, in fact, it may be a completely
// internally handled runtime exception, so we should do nothing.
//
if ((GetThreadNULLOk() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread()))
if ((GetThreadNULLOk() == NULL) || GetThread()->IsThrowableNull())
{
return EXCEPTION_CONTINUE_SEARCH;
}
Expand Down Expand Up @@ -12336,7 +12336,7 @@ bool Debugger::IsThreadAtSafePlace(Thread *thread)
// NOTE: don't check for thread->IsExceptionInProgress(), SO has special handling
// that directly sets the last thrown object without ever creating a tracker.
// (Tracker is what thread->IsExceptionInProgress() checks for)
if (g_pEEInterface->GetThreadException(thread) == CLRException::GetPreallocatedStackOverflowExceptionHandle())
if (thread->IsLastThrownObjectStackOverflowException())
{
return false;
}
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/vm/eedbginterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ class EEDebugInterface

virtual OBJECTHANDLE GetThreadException(Thread *pThread) = 0;

virtual bool IsThreadExceptionNull(Thread *pThread) = 0;

virtual void ClearThreadException(Thread *pThread) = 0;

virtual bool StartSuspendForDebug(AppDomain *pAppDomain,
Expand Down
14 changes: 3 additions & 11 deletions src/coreclr/vm/eedbginterfaceimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,20 +248,12 @@ OBJECTHANDLE EEDbgInterfaceImpl::GetThreadException(Thread *pThread)

// Return the last thrown object if there's no current throwable.
// This logic is similar to UpdateCurrentThrowable().
return pThread->m_LastThrownObjectHandle;
}

bool EEDbgInterfaceImpl::IsThreadExceptionNull(Thread *pThread)
{
CONTRACTL
if (!pThread->IsLastThrownObjectNull())
{
NOTHROW;
GC_NOTRIGGER;
PRECONDITION(CheckPointer(pThread));
return pThread->LastThrownObjectHandle();
}
CONTRACTL_END;

return pThread->IsThrowableNull();
return NULL;
}

void EEDbgInterfaceImpl::ClearThreadException(Thread *pThread)
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/vm/eedbginterfaceimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ class EEDbgInterfaceImpl : public EEDebugInterface

OBJECTHANDLE GetThreadException(Thread *pThread);

bool IsThreadExceptionNull(Thread *pThread);

void ClearThreadException(Thread *pThread);

bool StartSuspendForDebug(AppDomain *pAppDomain,
Expand Down
16 changes: 6 additions & 10 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2020,11 +2020,7 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable)

// Always save the current object in the handle so on rethrow we can reuse it. This is important as it
// contains stack trace info.
//
// Note: we use SafeSetLastThrownObject, which will try to set the throwable and if there are any problems,
// it will set the throwable to something appropriate (like OOM exception) and return the new
// exception. Thus, the user's exception object can be replaced here.
throwable = pThread->SafeSetLastThrownObject(throwable);
pThread->SetLastThrownObject(throwable);

ULONG_PTR hr = GetHRFromThrowable(throwable);

Expand Down Expand Up @@ -4007,7 +4003,7 @@ LONG InternalUnhandledExceptionFilter_Worker(
OBJECTREF oThrowable = pParam->pThread->GetThrowable();
if ((oThrowable != NULL) && (pParam->pThread->LastThrownObject() != oThrowable))
{
pParam->pThread->SafeSetLastThrownObject(oThrowable);
pParam->pThread->SetLastThrownObject(oThrowable);
LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Resetting the LastThrownObject as it appears to have changed.\n"));
}

Expand Down Expand Up @@ -8194,10 +8190,10 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
if (fAreBucketingDetailsPresent)
{
#ifdef _DEBUG
// Under OOM scenarios, its possible that when we are raising a threadabort,
// the throwable may get converted to preallocated OOM object when RaiseTheExceptionInternalOnly
// invokes Thread::SafeSetLastThrownObject. We check if this is the current case and use it in
// our validation below.
// It's possible that the throwable is the preallocated OOM object even when the
// bucket tracker captured the buckets for a thread abort (e.g., OOM was thrown
// directly while a thread abort was in flight). Account for that case in our
// validation below.
BOOL fIsPreallocatedOOMExceptionForTA = FALSE;
if ((!fIsThreadAbortException) && pUEWatsonBucketTracker->CapturedForThreadAbort())
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3186,7 +3186,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi

if (!pThread->GetExceptionState()->IsExceptionInProgress())
{
pThread->SafeSetLastThrownObject(NULL);
pThread->SetLastThrownObject(NULL);
}

// Sync managed exception state, for the managed thread, based upon any active exception tracker
Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10543,9 +10543,7 @@ void CEEInfo::HandleException(struct _EXCEPTION_POINTERS *pExceptionPointers)
if (_gc.oCurrentThrowable != _gc.oLastThrownObject)
{
// Update the LTO.
//
// Note: Incase of OOM, this will get set to OOM instance.
pCurThread->SafeSetLastThrownObject(_gc.oCurrentThrowable);
pCurThread->SetLastThrownObject(_gc.oCurrentThrowable);
}

GCPROTECT_END();
Expand Down
10 changes: 4 additions & 6 deletions src/coreclr/vm/prestub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1933,9 +1933,8 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method
}
EX_CATCH
{
OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle();
_ASSERTE(ohThrowable);
StackTraceInfo::AppendElement(ObjectFromHandle(ohThrowable), 0, (UINT_PTR)pTransitionBlock, pMD, NULL);
_ASSERTE(!CURRENT_THREAD->IsLastThrownObjectNull());
StackTraceInfo::AppendElement(CURRENT_THREAD->LastThrownObject(), 0, (UINT_PTR)pTransitionBlock, pMD, NULL);
EX_RETHROW;
}
EX_END_CATCH
Expand Down Expand Up @@ -2136,11 +2135,10 @@ void ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(PCODE portableE
}
EX_CATCH
{
OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle();
_ASSERTE(ohThrowable);
_ASSERTE(!CURRENT_THREAD->IsLastThrownObjectNull());
if (finishedPrestubPortion)
{
StackTraceInfo::AppendElement(ObjectFromHandle(ohThrowable), 0, (UINT_PTR)block, pMethod, NULL);
StackTraceInfo::AppendElement(CURRENT_THREAD->LastThrownObject(), 0, (UINT_PTR)block, pMethod, NULL);
}
EX_RETHROW;
}
Expand Down
129 changes: 24 additions & 105 deletions src/coreclr/vm/threads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,9 @@ Thread::Thread()
m_StrongHndToExposedObject = CreateGlobalStrongHandle(NULL);
GlobalStrongHandleHolder strongHndToExposedObjectHolder(m_StrongHndToExposedObject);

m_LastThrownObjectHandle = NULL;
m_LastThrownObjectHandle = CreateGlobalStrongHandle(NULL);
GlobalStrongHandleHolder lastThrownObjectHandleHolder(m_LastThrownObjectHandle);

m_ltoIsUnhandled = FALSE;

m_debuggerFilterContext = NULL;
Expand Down Expand Up @@ -1364,6 +1366,7 @@ Thread::Thread()
exposedObjectHolder.SuppressRelease();
strongHndToExposedObjectHolder.SuppressRelease();
contextHolder.SuppressRelease();
lastThrownObjectHandleHolder.SuppressRelease();

#ifdef FEATURE_COMINTEROP
m_uliInitializeSpyCookie.QuadPart = 0ul;
Expand Down Expand Up @@ -2270,11 +2273,13 @@ Thread::~Thread()

if (!IsAtProcessExit())
{
// Destroy any handles that we're using to hold onto exception objects
SafeSetThrowables(NULL);

DestroyShortWeakHandle(m_ExposedObject);
DestroyStrongHandle(m_StrongHndToExposedObject);

if (m_LastThrownObjectHandle != g_pPreallocatedStackOverflowException)
{
DestroyStrongHandle(m_LastThrownObjectHandle);
}
}

g_pThinLockThreadIdDispenser->DisposeId(GetThreadId());
Expand Down Expand Up @@ -3111,7 +3116,7 @@ void Thread::SetLastThrownObject(OBJECTREF throwable, BOOL isUnhandled)
{
CONTRACTL
{
if ((throwable == NULL) || CLRException::IsPreallocatedExceptionObject(throwable)) NOTHROW; else THROWS; // From CreateHandle
NOTHROW;
GC_NOTRIGGER;
if (throwable == NULL) MODE_ANY; else MODE_COOPERATIVE;
}
Expand All @@ -3125,44 +3130,22 @@ void Thread::SetLastThrownObject(OBJECTREF throwable, BOOL isUnhandled)
// you can't have a NULL unhandled exception
_ASSERTE(!(throwable == NULL && isUnhandled));

if (m_LastThrownObjectHandle != NULL)
{
// We'll sometimes use a handle for a preallocated exception object. We should never, ever destroy one of
// these handles... they'll be destroyed when the Runtime shuts down.
if (!CLRException::IsPreallocatedExceptionHandle(m_LastThrownObjectHandle))
{
DestroyHandle(m_LastThrownObjectHandle);
}

m_LastThrownObjectHandle = NULL; // Make sure to set this to NULL here just in case we throw trying to make
// a new handle below.
}
// Each Thread owns a per-thread strong handle for the LastThrownObject slot
// (allocated in the Thread ctor, destroyed in ~Thread). Setting LTO is just
// a payload write; no handle allocation or destruction is needed.
_ASSERTE(m_LastThrownObjectHandle != NULL);
_ASSERTE(throwable == NULL || this == GetThread());
_ASSERTE(throwable == NULL || IsException(throwable->GetMethodTable()));

if (throwable != NULL)
{
_ASSERTE(this == GetThread());

// Non-compliant exceptions are always wrapped.
// The use of the ExceptionNative:: helper here (rather than the global ::IsException helper)
// is hokey, but we need a GC_NOTRIGGER version and it's only for an ASSERT.
_ASSERTE(IsException(throwable->GetMethodTable()));

// If we're tracking one of the preallocated exception objects, then just use the global handle that
// matches it rather than creating a new one.
if (CLRException::IsPreallocatedExceptionObject(throwable))
{
m_LastThrownObjectHandle = CLRException::GetPreallocatedHandleForObject(throwable);
}
else
{
m_LastThrownObjectHandle = AppDomain::GetCurrentDomain()->CreateHandle(throwable);
}

_ASSERTE(m_LastThrownObjectHandle != NULL);
StoreObjectInHandle(m_LastThrownObjectHandle, throwable);
m_ltoIsUnhandled = isUnhandled;

}
else
{
ResetOBJECTHANDLE(m_LastThrownObjectHandle);
m_ltoIsUnhandled = FALSE;
}
}
Expand All @@ -3185,41 +3168,6 @@ void Thread::SetSOForLastThrownObject()
m_LastThrownObjectHandle = CLRException::GetPreallocatedStackOverflowExceptionHandle();
}

//
// This is a nice wrapper for SetLastThrownObject which catches any exceptions caused by not being able to create
// the handle for the throwable, and setting the last thrown object to the preallocated out of memory exception
// instead.
//
OBJECTREF Thread::SafeSetLastThrownObject(OBJECTREF throwable)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
if (throwable == NULL) MODE_ANY; else MODE_COOPERATIVE;
}
CONTRACTL_END;

// We return the original throwable if nothing goes wrong.
OBJECTREF ret = throwable;

EX_TRY
{
// Try to set the throwable.
SetLastThrownObject(throwable);
}
EX_CATCH
{
// If it didn't work, then set the last thrown object to the preallocated OOM exception, and return that
// object instead of the original throwable.
ret = CLRException::GetPreallocatedOutOfMemoryException();
SetLastThrownObject(ret);
}
EX_END_CATCH

return ret;
}

//
// This is a nice wrapper for updating the last thrown object handle, which catches any exceptions caused by not
// being able to create the handle for the throwable, and falls back to the preallocated out of memory exception
Expand Down Expand Up @@ -3283,34 +3231,15 @@ void Thread::SyncManagedExceptionState(bool fIsDebuggerThread)
GCX_COOP();

// Syncup the LastThrownObject on the managed thread
SafeUpdateLastThrownObject();
UpdateLastThrownObject();
}
}

void Thread::SetLastThrownObjectHandle(OBJECTHANDLE h)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACTL_END;

if (m_LastThrownObjectHandle != NULL &&
!CLRException::IsPreallocatedExceptionHandle(m_LastThrownObjectHandle))
{
DestroyHandle(m_LastThrownObjectHandle);
}

m_LastThrownObjectHandle = h;
}

//
// Create a duplicate handle of the current throwable and set the last thrown object to that. This ensures that the
// last thrown object and the current throwable have handles that are in the same app domain.
//
void Thread::SafeUpdateLastThrownObject(void)
void Thread::UpdateLastThrownObject(void)
{
CONTRACTL
{
Expand All @@ -3324,16 +3253,7 @@ void Thread::SafeUpdateLastThrownObject(void)

if (throwable != NULL)
{
EX_TRY
{
SetLastThrownObject(throwable);
}
EX_CATCH
{
// If we can't create a handle, set the last thrown object to the preallocated OOM exception.
SafeSetThrowables(CLRException::GetPreallocatedOutOfMemoryException());
}
EX_END_CATCH
SetLastThrownObject(throwable);
}
}

Expand Down Expand Up @@ -6690,9 +6610,8 @@ extern "C" InterpThreadContext* STDCALL GetInterpThreadContextWithPossiblyMissin
}
EX_CATCH
{
OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle();
_ASSERTE(ohThrowable);
StackTraceInfo::AppendElement(ObjectFromHandle(ohThrowable), 0, (UINT_PTR)pTransitionBlock, pByteCodeStart->Method->methodHnd, NULL);
_ASSERTE(!CURRENT_THREAD->IsLastThrownObjectNull());
StackTraceInfo::AppendElement(CURRENT_THREAD->LastThrownObject(), 0, (UINT_PTR)pTransitionBlock, pByteCodeStart->Method->methodHnd, NULL);
EX_RETHROW;
}
EX_END_CATCH
Expand Down
Loading
Loading