Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,17 @@ private unsafe void StartCore()
{
fixed (char* pThreadName = _name)
{
StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, _isThreadPool ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, pThreadName);
Exception? exception = null;
if (StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, _isThreadPool ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, pThreadName, ObjectHandleOnStack.Create(ref exception)) == Interop.BOOL.FALSE)
{
throw new ThreadStartException(exception);
}
}
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")]
private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, Interop.BOOL isThreadPool, char* pThreadName);
private static unsafe partial Interop.BOOL StartInternal(ThreadHandle t, int stackSize, int priority, Interop.BOOL isThreadPool, char* pThreadName, ObjectHandleOnStack exception);

// Called from the runtime
private void StartCallback()
Expand Down
9 changes: 7 additions & 2 deletions src/coreclr/vm/comsynchronizable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,12 @@ static ULONG WINAPI KickOffThread(void* pass)
return 0;
}

extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName)
extern "C" BOOL QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName, QCall::ObjectHandleOnStack exception)
{
QCALL_CONTRACT;

BOOL result = TRUE;

BEGIN_QCALL;

Thread* pNewThread = thread;
Expand Down Expand Up @@ -266,10 +268,13 @@ extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int thr
{
GCX_COOP();

pNewThread->HandleThreadStartupFailure();
result = FALSE;
exception.Set(pNewThread->GetExceptionDuringStartup());
}

END_QCALL;

return result;
}

extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/comsynchronizable.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ThreadNative
static FCDECL0(FC_BOOL_RET, CurrentThreadIsFinalizerThread);
};

extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName);
extern "C" BOOL QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName, QCall::ObjectHandleOnStack exception);
extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority);
extern "C" void QCALLTYPE ThreadNative_GetCurrentThread(QCall::ObjectHandleOnStack thread);
extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle thread);
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,6 @@ DEFINE_METHOD(SYSTEM_EXCEPTION, STR_EX_CTOR, .ctor,
DEFINE_CLASS(TYPE_INIT_EXCEPTION, System, TypeInitializationException)
DEFINE_METHOD(TYPE_INIT_EXCEPTION, STR_EX_CTOR, .ctor, IM_Str_Exception_RetVoid)

DEFINE_CLASS(THREAD_START_EXCEPTION,Threading, ThreadStartException)
DEFINE_METHOD(THREAD_START_EXCEPTION,EX_CTOR, .ctor, IM_Exception_RetVoid)

DEFINE_CLASS(VALUETASK_1, Tasks, ValueTask`1)
DEFINE_METHOD(VALUETASK_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
DEFINE_METHOD(VALUETASK_1, GET_RESULT, get_Result, NoSig)
Expand Down
219 changes: 47 additions & 172 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ BOOL IsExceptionFromManagedCode(const EXCEPTION_RECORD * pExceptionRecord)
PEXCEPTION_REGISTRATION_RECORD GetCurrentSEHRecord();
BOOL IsUnmanagedToManagedSEHHandler(EXCEPTION_REGISTRATION_RECORD*);

VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow);

//-------------------------------------------------------------------------------
// Basically, this asks whether the exception is a managed exception thrown by
// this instance of the CLR.
Expand Down Expand Up @@ -1965,61 +1963,6 @@ ReplaceExceptionContextRecord(CONTEXT *pTarget, CONTEXT *pSource)
#endif // !CONTEXT_EXTENDED_REGISTERS
}

VOID FixupOnRethrow(Thread* pCurThread, EXCEPTION_POINTERS* pExceptionPointers)
{
WRAPPER_NO_CONTRACT;

ThreadExceptionState* pExState = pCurThread->GetExceptionState();

// Don't allow rethrow of a STATUS_STACK_OVERFLOW -- it's a new throw of the CLR exception.
if (pExState->GetExceptionCode() == STATUS_STACK_OVERFLOW)
{
return;
}

// For COMPLUS exceptions, we don't need the original context for our rethrow.
if (!(pExState->IsComPlusException()))
{
_ASSERTE(pExState->GetExceptionRecord());

// don't copy parm args as have already supplied them on the throw
memcpy((void*)pExceptionPointers->ExceptionRecord,
(void*)pExState->GetExceptionRecord(),
offsetof(EXCEPTION_RECORD, ExceptionInformation));
}

pExState->GetFlags()->SetIsRethrown();
}

struct RaiseExceptionFilterParam
{
BOOL isRethrown;
};

LONG RaiseExceptionFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;

RaiseExceptionFilterParam *pParam = (RaiseExceptionFilterParam *) pv;

if (1 == pParam->isRethrown)
{
// need to reset the EH info back to the original thrown exception
FixupOnRethrow(GetThread(), ep);

// only do this once
pParam->isRethrown++;
}
else
{
CONSISTENCY_CHECK((2 == pParam->isRethrown) || (0 == pParam->isRethrown));
}

return EXCEPTION_CONTINUE_SEARCH;
}

HRESULT GetHRFromThrowable(OBJECTREF throwable)
{
STATIC_CONTRACT_THROWS;
Expand All @@ -2041,14 +1984,14 @@ HRESULT GetHRFromThrowable(OBJECTREF throwable)
return hr;
}

VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL rethrow, BOOL fForStackOverflow)
VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_COOPERATIVE;

STRESS_LOG3(LF_EH, LL_INFO100, "******* MANAGED EXCEPTION THROWN: Object thrown: %p MT %pT rethrow %d\n",
OBJECTREFToObject(throwable), (throwable!=0)?throwable->GetMethodTable():0, rethrow);
STRESS_LOG2(LF_EH, LL_INFO100, "******* MANAGED EXCEPTION THROWN: Object thrown: %p MT %pT\n",
OBJECTREFToObject(throwable), (throwable!=0)?throwable->GetMethodTable():0);

#ifdef STRESS_LOG
// Any object could have been thrown, but System.Exception objects have useful information for the stress log
Expand All @@ -2071,34 +2014,24 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL r
}
#endif

struct Param : RaiseExceptionFilterParam
{
OBJECTREF throwable;
BOOL fForStackOverflow;
ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE];
Thread *pThread;
ThreadExceptionState* pExState;
} param;
param.isRethrown = rethrow ? 1 : 0; // normalize because we use it as a count in RaiseExceptionFilter
param.throwable = throwable;
param.fForStackOverflow = fForStackOverflow;
param.pThread = GetThread();
ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE];
Thread* pThread = GetThread();

_ASSERTE(param.pThread);
param.pExState = param.pThread->GetExceptionState();
_ASSERTE(pThread);
ThreadExceptionState* pExState = pThread->GetExceptionState();

if (param.pThread->IsRudeAbortInitiated())
if (pThread->IsRudeAbortInitiated())
{
// Nobody should be able to swallow rude thread abort.
param.throwable = CLRException::GetBestThreadAbortException();
throwable = CLRException::GetBestThreadAbortException();
}

#if 0
// TODO: enable this after we change RealCOMPlusThrow
#ifdef _DEBUG
// If ThreadAbort exception is thrown, the thread should be marked with AbortRequest.
// If not, we may see unhandled exception.
if (param.throwable->GetMethodTable() == g_pThreadAbortExceptionClass)
if (throwable->GetMethodTable() == g_pThreadAbortExceptionClass)
{
_ASSERTE(GetThread()->IsAbortRequested()
#ifdef TARGET_X86
Expand All @@ -2111,76 +2044,41 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL r
#endif

// raise
PAL_TRY(Param *, pParam, &param)
{
//_ASSERTE(! pParam->isRethrown || pParam->pExState->m_pExceptionRecord);
ULONG_PTR *args = NULL;
ULONG argCount = 0;
ULONG flags = 0;
ULONG code = 0;

// 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.
pParam->throwable = pParam->pThread->SafeSetLastThrownObject(pParam->throwable);

if (!pParam->isRethrown ||
pParam->pExState->IsComPlusException() ||
(pParam->pExState->GetExceptionCode() == STATUS_STACK_OVERFLOW))
{
ULONG_PTR hr = GetHRFromThrowable(pParam->throwable);

args = pParam->exceptionArgs;
argCount = MarkAsThrownByUs(args, hr);
flags = EXCEPTION_NONCONTINUABLE;
code = EXCEPTION_COMPLUS;
}
else
{
// Exception code should be consistent.
_ASSERTE((DWORD)(pParam->pExState->GetExceptionRecord()->ExceptionCode) == pParam->pExState->GetExceptionCode());
ULONG argCount = 0;
ULONG flags = 0;
ULONG code = 0;

args = pParam->pExState->GetExceptionRecord()->ExceptionInformation;
argCount = pParam->pExState->GetExceptionRecord()->NumberParameters;
flags = pParam->pExState->GetExceptionRecord()->ExceptionFlags;
code = pParam->pExState->GetExceptionRecord()->ExceptionCode;
}
// 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);

if (pParam->pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&pParam->throwable))
{
pParam->pThread->ResetPreparingAbort();
ULONG_PTR hr = GetHRFromThrowable(throwable);

if (pParam->pThread->GetFrame() == FRAME_TOP)
{
// There is no more managed code on stack.
pParam->pThread->ResetAbort();
}
}
argCount = MarkAsThrownByUs(exceptionArgs, hr);
flags = EXCEPTION_NONCONTINUABLE;
code = EXCEPTION_COMPLUS;

// Can't access the exception object when are in pre-emptive, so find out before
// if its an SO.
BOOL fIsStackOverflow = IsExceptionOfType(kStackOverflowException, &pParam->throwable);
if (pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&throwable))
{
pThread->ResetPreparingAbort();

if (fIsStackOverflow || pParam->fForStackOverflow)
if (pThread->GetFrame() == FRAME_TOP)
{
// Don't probe if we're already handling an SO. Just throw the exception.
RaiseException(code, flags, argCount, args);
// There is no more managed code on stack.
pThread->ResetAbort();
}
}

// This needs to be both here and inside the handler below
// enable preemptive mode before call into OS
GCX_PREEMP_NO_DTOR();
// Enable preemptive mode before call into OS
GCX_PREEMP_NO_DTOR();

RaiseException(code, flags, argCount, exceptionArgs);

// In non-debug, we can just raise the exception once we've probed.
RaiseException(code, flags, argCount, args);
}
PAL_EXCEPT_FILTER (RaiseExceptionFilter)
{
}
PAL_ENDTRY
_ASSERTE(!"Cannot continue after CLR exception"); // Debugger can bring you here.
// For example,
// Debugger breaks in due to second chance exception (unhandled)
Expand All @@ -2190,7 +2088,7 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL r
UNREACHABLE();
}

static VOID DECLSPEC_NORETURN RealCOMPlusThrowWorker(OBJECTREF throwable, BOOL rethrow)
static VOID DECLSPEC_NORETURN RealCOMPlusThrowWorker(OBJECTREF throwable)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
Expand All @@ -2208,10 +2106,10 @@ static VOID DECLSPEC_NORETURN RealCOMPlusThrowWorker(OBJECTREF throwable, BOOL r
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
}

RaiseTheExceptionInternalOnly(throwable, rethrow);
RaiseTheExceptionInternalOnly(throwable);
}

VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow)
VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
Expand All @@ -2224,41 +2122,18 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow)

_ASSERTE(IsException(throwable->GetMethodTable()));

// This may look a bit odd, but there is an explanation. The rethrow boolean
// means that an actual RaiseException(EXCEPTION_COMPLUS,...) is being re-thrown,
// and that the exception context saved on the Thread object should replace
// the exception context from the upcoming RaiseException(). There is logic
// in the stack trace code to preserve MOST of the stack trace, but to drop the
// last element of the stack trace (has to do with having the address of the rethrow
// instead of the address of the original call in the stack trace. That is
// controversial itself, but we won't get into that here.)
// However, if this is not re-raising that original exception, but rather a new
// If this is not re-raising that original exception, but rather a new
// os exception for what may be an existing exception object, it is generally
// a good thing to preserve the stack trace.
if (!rethrow)
{
Thread *pThread = GetThread();
pThread->IncPreventAbort();
ExceptionPreserveStackTrace(throwable);
pThread->DecPreventAbort();
}

RealCOMPlusThrowWorker(throwable, rethrow);

GCPROTECT_END();
}
Thread *pThread = GetThread();
pThread->IncPreventAbort();
ExceptionPreserveStackTrace(throwable);
pThread->DecPreventAbort();

VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
RealCOMPlusThrowWorker(throwable);

RealCOMPlusThrow(throwable, FALSE);
GCPROTECT_END();
}

#ifdef TARGET_WASM
Expand Down Expand Up @@ -2295,7 +2170,7 @@ VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object *
#else
OBJECTREF throwable = ObjectToOBJECTREF(exceptionObj);
#endif // TARGET_WASM
RealCOMPlusThrowWorker(throwable, FALSE);
RealCOMPlusThrowWorker(throwable);
#ifdef TARGET_WASM
}
PAL_EXCEPT(SetTargetFrame(ex, __param->targetSP))
Expand Down Expand Up @@ -6910,7 +6785,7 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra
}
else
{
RaiseTheExceptionInternalOnly(orThrowable, FALSE);
RaiseTheExceptionInternalOnly(orThrowable);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/exceptmacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ VEH_ACTION CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo);
// Actual UEF worker prototype for use by GCUnhandledExceptionFilter.
extern LONG InternalUnhandledExceptionFilter_Worker(PEXCEPTION_POINTERS pExceptionInfo);

VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL rethrow, BOOL fForStackOverflow = FALSE);
VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable);

#if defined(DACCESS_COMPILE)

Expand Down
Loading
Loading