summaryrefslogtreecommitdiff
path: root/src/vm/comsynchronizable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/comsynchronizable.cpp')
-rw-r--r--src/vm/comsynchronizable.cpp2243
1 files changed, 2243 insertions, 0 deletions
diff --git a/src/vm/comsynchronizable.cpp b/src/vm/comsynchronizable.cpp
new file mode 100644
index 0000000000..ef195bf5de
--- /dev/null
+++ b/src/vm/comsynchronizable.cpp
@@ -0,0 +1,2243 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+/*============================================================
+**
+** Header: COMSynchronizable.cpp
+**
+** Purpose: Native methods on System.SynchronizableObject
+** and its subclasses.
+**
+**
+===========================================================*/
+
+#include "common.h"
+
+#include <object.h>
+#include "threads.h"
+#include "excep.h"
+#include "vars.hpp"
+#include "field.h"
+#include "security.h"
+#include "comsynchronizable.h"
+#include "dbginterface.h"
+#include "comdelegate.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "eeconfig.h"
+#include "stackcompressor.h"
+#ifdef FEATURE_REMOTING
+#include "appdomainhelper.h"
+#include "objectclone.h"
+#else
+#include "callhelpers.h"
+#endif
+#include "appdomain.hpp"
+#include "appdomain.inl"
+#ifdef FEATURE_REMOTING
+#include "crossdomaincalls.h"
+#endif
+
+#include "newapis.h"
+
+// To include definition of CAPTURE_BUCKETS_AT_TRANSITION
+#include "exstate.h"
+
+// The two threads need to communicate some information. Any object references must
+// be declared to GC.
+struct SharedState
+{
+ OBJECTHANDLE m_Threadable;
+ OBJECTHANDLE m_ThreadStartArg;
+ Thread *m_Internal;
+ OBJECTHANDLE m_Principal;
+
+ SharedState(OBJECTREF threadable, OBJECTREF threadStartArg, Thread *internal, OBJECTREF principal)
+ {
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS; // From CreateHandle()
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+ AppDomainFromIDHolder ad(internal->GetKickOffDomainId(), TRUE);
+ if (ad.IsUnloaded())
+ COMPlusThrow(kAppDomainUnloadedException);
+
+ m_Threadable = ad->CreateHandle(threadable);
+ m_ThreadStartArg = ad->CreateHandle(threadStartArg);
+
+ m_Internal = internal;
+
+ m_Principal = ad->CreateHandle(principal);
+ }
+
+ ~SharedState()
+ {
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ // It's important to have no GC rendez-vous point between the checking and the clean-up below.
+ // The three handles below could be in an appdomain which is just starting to be unloaded, or an appdomain
+ // which has been unloaded already. Thus, we need to check whether the appdomain is still valid before
+ // we do the clean-up. Since we suspend all runtime threads when we try to do the unload, there will be no
+ // race condition between the checking and the clean-up as long as this thread cannot be suspended in between.
+ AppDomainFromIDHolder ad(m_Internal->GetKickOffDomainId(), TRUE);
+ if (!ad.IsUnloaded())
+ {
+ DestroyHandle(m_Threadable);
+ DestroyHandle(m_ThreadStartArg);
+ DestroyHandle(m_Principal);
+ }
+ }
+};
+
+
+// For the following helpers, we make no attempt to synchronize. The app developer
+// is responsible for managing his own race conditions.
+//
+// Note: if the internal Thread is NULL, this implies that the exposed object has
+// finalized and then been resurrected.
+static inline BOOL ThreadNotStarted(Thread *t)
+{
+ WRAPPER_NO_CONTRACT;
+ return (t && t->IsUnstarted() && !t->HasValidThreadHandle());
+}
+
+static inline BOOL ThreadIsRunning(Thread *t)
+{
+ WRAPPER_NO_CONTRACT;
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ return (t &&
+ (t->m_State & (Thread::TS_ReportDead|Thread::TS_Dead)) == 0 &&
+ (CLRTaskHosted()? t->GetHostTask()!=NULL:t->HasValidThreadHandle()));
+#else // !FEATURE_INCLUDE_ALL_INTERFACES
+ return (t &&
+ (t->m_State & (Thread::TS_ReportDead|Thread::TS_Dead)) == 0 &&
+ (t->HasValidThreadHandle()));
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+}
+
+static inline BOOL ThreadIsDead(Thread *t)
+{
+ WRAPPER_NO_CONTRACT;
+ return (t == 0 || t->IsDead());
+}
+
+
+// Map our exposed notion of thread priorities into the enumeration that NT uses.
+static INT32 MapToNTPriority(INT32 ours)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ INT32 NTPriority = 0;
+
+ switch (ours)
+ {
+ case ThreadNative::PRIORITY_LOWEST:
+ NTPriority = THREAD_PRIORITY_LOWEST;
+ break;
+
+ case ThreadNative::PRIORITY_BELOW_NORMAL:
+ NTPriority = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+
+ case ThreadNative::PRIORITY_NORMAL:
+ NTPriority = THREAD_PRIORITY_NORMAL;
+ break;
+
+ case ThreadNative::PRIORITY_ABOVE_NORMAL:
+ NTPriority = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+
+ case ThreadNative::PRIORITY_HIGHEST:
+ NTPriority = THREAD_PRIORITY_HIGHEST;
+ break;
+
+ default:
+ COMPlusThrow(kArgumentOutOfRangeException, W("Argument_InvalidFlag"));
+ }
+ return NTPriority;
+}
+
+
+// Map to our exposed notion of thread priorities from the enumeration that NT uses.
+INT32 MapFromNTPriority(INT32 NTPriority)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ INT32 ours = 0;
+
+ if (NTPriority <= THREAD_PRIORITY_LOWEST)
+ {
+ // managed code does not support IDLE. Map it to PRIORITY_LOWEST.
+ ours = ThreadNative::PRIORITY_LOWEST;
+ }
+ else if (NTPriority >= THREAD_PRIORITY_HIGHEST)
+ {
+ ours = ThreadNative::PRIORITY_HIGHEST;
+ }
+ else if (NTPriority == THREAD_PRIORITY_BELOW_NORMAL)
+ {
+ ours = ThreadNative::PRIORITY_BELOW_NORMAL;
+ }
+ else if (NTPriority == THREAD_PRIORITY_NORMAL)
+ {
+ ours = ThreadNative::PRIORITY_NORMAL;
+ }
+ else if (NTPriority == THREAD_PRIORITY_ABOVE_NORMAL)
+ {
+ ours = ThreadNative::PRIORITY_ABOVE_NORMAL;
+ }
+ else
+ {
+ _ASSERTE (!"not supported priority");
+ ours = ThreadNative::PRIORITY_NORMAL;
+ }
+ return ours;
+}
+
+
+void ThreadNative::KickOffThread_Worker(LPVOID ptr)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ KickOffThread_Args *args = (KickOffThread_Args *) ptr;
+ _ASSERTE(ObjectFromHandle(args->share->m_Threadable) != NULL);
+ args->retVal = 0;
+
+ // we are saving the delagate and result primarily for debugging
+ struct _gc
+ {
+ OBJECTREF orPrincipal;
+ OBJECTREF orThreadStartArg;
+ OBJECTREF orDelegate;
+ OBJECTREF orResult;
+ OBJECTREF orThread;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ Thread *pThread;
+ pThread = GetThread();
+ _ASSERTE(pThread);
+ GCPROTECT_BEGIN(gc);
+ BEGIN_SO_INTOLERANT_CODE(pThread);
+
+ gc.orPrincipal = ObjectFromHandle(args->share->m_Principal);
+
+#ifdef FEATURE_IMPERSONATION
+ // Push the initial security principal object (if any) onto the
+ // managed thread.
+ if (gc.orPrincipal != NULL)
+ {
+ gc.orThread = args->pThread->GetExposedObject();
+ MethodDescCallSite setPrincipalInternal(METHOD__THREAD__SET_PRINCIPAL_INTERNAL, &gc.orThread);
+ ARG_SLOT argsToSetPrincipal[2];
+ argsToSetPrincipal[0] = ObjToArgSlot(gc.orThread);
+ argsToSetPrincipal[1] = ObjToArgSlot(gc.orPrincipal);
+ setPrincipalInternal.Call(argsToSetPrincipal);
+ }
+#endif
+
+ gc.orDelegate = ObjectFromHandle(args->share->m_Threadable);
+ gc.orThreadStartArg = ObjectFromHandle(args->share->m_ThreadStartArg);
+
+ // We cannot call the Delegate Invoke method directly from ECall. The
+ // stub has not been created for non multicast delegates. Instead, we
+ // will invoke the Method on the OR stored in the delegate directly.
+ // If there are changes to the signature of the ThreadStart delegate
+ // this code will need to change. I've noted this in the Thread start
+ // class.
+
+ delete args->share;
+ args->share = 0;
+
+ MethodDesc *pMeth = ((DelegateEEClass*)( gc.orDelegate->GetMethodTable()->GetClass() ))->m_pInvokeMethod;
+ _ASSERTE(pMeth);
+ MethodDescCallSite invokeMethod(pMeth, &gc.orDelegate);
+
+ if (MscorlibBinder::IsClass(gc.orDelegate->GetMethodTable(), CLASS__PARAMETERIZEDTHREADSTART))
+ {
+ //Parameterized ThreadStart
+ ARG_SLOT arg[2];
+
+ arg[0] = ObjToArgSlot(gc.orDelegate);
+ arg[1]=ObjToArgSlot(gc.orThreadStartArg);
+ invokeMethod.Call(arg);
+ }
+ else
+ {
+ //Simple ThreadStart
+ ARG_SLOT arg[1];
+
+ arg[0] = ObjToArgSlot(gc.orDelegate);
+ invokeMethod.Call(arg);
+ }
+ STRESS_LOG2(LF_SYNC, LL_INFO10, "Managed thread exiting normally for delegate %p Type %pT\n", OBJECTREFToObject(gc.orDelegate), (size_t) gc.orDelegate->GetMethodTable());
+
+ END_SO_INTOLERANT_CODE;
+ GCPROTECT_END();
+}
+
+// Helper to avoid two EX_TRY/EX_CATCH blocks in one function
+static void PulseAllHelper(Thread* pThread)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ DISABLED(NOTHROW);
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ EX_TRY
+ {
+ // GetExposedObject() will either throw, or we have a valid object. Note
+ // that we re-acquire it each time, since it may move during calls.
+ pThread->GetExposedObject()->EnterObjMonitor();
+ pThread->GetExposedObject()->PulseAll();
+ pThread->GetExposedObject()->LeaveObjMonitor();
+ }
+ EX_CATCH
+ {
+ // just keep going...
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+}
+
+// When an exposed thread is started by Win32, this is where it starts.
+ULONG __stdcall ThreadNative::KickOffThread(void* pass)
+{
+
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ ULONG retVal = 0;
+ // Before we do anything else, get Setup so that we have a real thread.
+
+ // Our thread isn't setup yet, so we can't use the standard probe
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return E_FAIL);
+
+ KickOffThread_Args args;
+ // don't have a separate var becuase this can be updated in the worker
+ args.share = (SharedState *) pass;
+ args.pThread = args.share->m_Internal;
+
+ Thread* pThread = args.pThread;
+
+ _ASSERTE(pThread != NULL);
+
+ BOOL ok = TRUE;
+
+ {
+ EX_TRY
+ {
+ CExecutionEngine::CheckThreadState(0);
+ }
+ EX_CATCH
+ {
+ // OOM might be thrown from CheckThreadState, so it's important
+ // that we don't rethrow it; if we do then the process will die
+ // because there are no installed handlers at this point, so
+ // swallow the exception. this will set the thread's state to
+ // FailStarted which will result in a ThreadStartException being
+ // thrown from the thread that attempted to start this one.
+ if (!GET_EXCEPTION()->IsTransient() && !SwallowUnhandledExceptions())
+ EX_RETHROW;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ if (CExecutionEngine::CheckThreadStateNoCreate(0) == NULL)
+ {
+ // We can not
+ pThread->SetThreadState(Thread::TS_FailStarted);
+ pThread->DetachThread(FALSE);
+ // !!! Do not touch any field of Thread object. The Thread object is subject to delete
+ // !!! after DetachThread call.
+ ok = FALSE;
+ }
+ }
+
+ if (ok)
+ {
+ ok = pThread->HasStarted();
+ }
+
+ if (ok)
+ {
+ // Do not swallow the unhandled exception here
+ //
+
+ // Fire ETW event to correlate with the thread that created current thread
+ if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadRunning))
+ FireEtwThreadRunning(pThread, GetClrInstanceId());
+
+ // We have a sticky problem here.
+ //
+ // Under some circumstances, the context of 'this' doesn't match the context
+ // of the thread. Today this can only happen if the thread is marked for an
+ // STA. If so, the delegate that is stored in the object may not be directly
+ // suitable for invocation. Instead, we need to call through a proxy so that
+ // the correct context transitions occur.
+ //
+ // All the changes occur inside HasStarted(), which will switch this thread
+ // over to a brand new STA as necessary. We have to notice this happening, so
+ // we can adjust the delegate we are going to invoke on.
+
+ _ASSERTE(GetThread() == pThread); // Now that it's started
+ ManagedThreadBase::KickOff(pThread->GetKickOffDomainId(), KickOffThread_Worker, &args);
+
+ // If TS_FailStarted is set then the args are deleted in ThreadNative::StartInner
+ if ((args.share) && !pThread->HasThreadState(Thread::TS_FailStarted))
+ {
+ delete args.share;
+ }
+
+ PulseAllHelper(pThread);
+
+ GCX_PREEMP_NO_DTOR();
+
+ pThread->ClearThreadCPUGroupAffinity();
+
+ DestroyThread(pThread);
+ }
+
+ END_SO_INTOLERANT_CODE;
+
+ return retVal;
+}
+
+
+FCIMPL3(void, ThreadNative::Start, ThreadBaseObject* pThisUNSAFE, Object* pPrincipalUNSAFE, StackCrawlMark* pStackMark)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_NOPOLL();
+
+ StartInner(pThisUNSAFE, pPrincipalUNSAFE, pStackMark);
+
+ HELPER_METHOD_FRAME_END_POLL();
+}
+FCIMPLEND
+
+// Start up a thread, which by now should be in the ThreadStore's Unstarted list.
+void ThreadNative::StartInner(ThreadBaseObject* pThisUNSAFE, Object* pPrincipalUNSAFE, StackCrawlMark* pStackMark)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ struct _gc
+ {
+ OBJECTREF pPrincipal;
+ THREADBASEREF pThis;
+ } gc;
+
+ gc.pPrincipal = (OBJECTREF) pPrincipalUNSAFE;
+ gc.pThis = (THREADBASEREF) pThisUNSAFE;
+
+ GCPROTECT_BEGIN(gc);
+
+ if (gc.pThis == NULL)
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+
+ Thread *pNewThread = gc.pThis->GetInternal();
+ if (pNewThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ _ASSERTE(GetThread() != NULL); // Current thread wandered in!
+
+ gc.pThis->EnterObjMonitor();
+
+ EX_TRY
+ {
+ // Is the thread already started? You can't restart a thread.
+ if (!ThreadNotStarted(pNewThread))
+ {
+ COMPlusThrow(kThreadStateException, IDS_EE_THREADSTART_STATE);
+ }
+
+ OBJECTREF threadable = gc.pThis->GetDelegate();
+ OBJECTREF threadStartArg = gc.pThis->GetThreadStartArg();
+ gc.pThis->SetDelegate(NULL);
+ gc.pThis->SetThreadStartArg(NULL);
+
+ // This can never happen, because we construct it with a valid one and then
+ // we never let you change it (because SetStart is private).
+ _ASSERTE(threadable != NULL);
+
+ // Allocate this away from our stack, so we can unwind without affecting
+ // KickOffThread. It is inside a GCFrame, so we can enable GC now.
+ NewHolder<SharedState> share(new SharedState(threadable, threadStartArg, pNewThread, gc.pPrincipal));
+
+ pNewThread->IncExternalCount();
+
+ // Fire an ETW event to mark the current thread as the launcher of the new thread
+ if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadCreating))
+ FireEtwThreadCreating(pNewThread, GetClrInstanceId());
+
+ // As soon as we create the new thread, it is eligible for suspension, etc.
+ // So it gets transitioned to cooperative mode before this call returns to
+ // us. It is our duty to start it running immediately, so that GC isn't blocked.
+
+ BOOL success = pNewThread->CreateNewThread(
+ pNewThread->RequestedThreadStackSize() /* 0 stackSize override*/,
+ KickOffThread, share);
+
+ if (!success)
+ {
+ pNewThread->DecExternalCount(FALSE);
+ COMPlusThrowOM();
+ }
+
+ // After we have established the thread handle, we can check m_Priority.
+ // This ordering is required to eliminate the race condition on setting the
+ // priority of a thread just as it starts up.
+ pNewThread->SetThreadPriority(MapToNTPriority(gc.pThis->m_Priority));
+ pNewThread->ChooseThreadCPUGroupAffinity();
+
+ FastInterlockOr((ULONG *) &pNewThread->m_State, Thread::TS_LegalToJoin);
+
+ DWORD ret;
+ ret = pNewThread->StartThread();
+
+ // When running under a user mode native debugger there is a race
+ // between the moment we've created the thread (in CreateNewThread) and
+ // the moment we resume it (in StartThread); the debugger may receive
+ // the "ct" (create thread) notification, and it will attempt to
+ // suspend/resume all threads in the process. Now imagine the debugger
+ // resumes this thread first, and only later does it try to resume the
+ // newly created thread. In these conditions our call to ResumeThread
+ // may come before the debugger's call to ResumeThread actually causing
+ // ret to equal 2.
+ // We cannot use IsDebuggerPresent() in the condition below because the
+ // debugger may have been detached between the time it got the notification
+ // and the moment we execute the test below.
+ _ASSERTE(ret == 1 || ret == 2);
+
+ {
+ GCX_PREEMP();
+
+ // Synchronize with HasStarted.
+ YIELD_WHILE (!pNewThread->HasThreadState(Thread::TS_FailStarted) &&
+ pNewThread->HasThreadState(Thread::TS_Unstarted));
+ }
+
+ if (!pNewThread->HasThreadState(Thread::TS_FailStarted))
+ {
+ share.SuppressRelease(); // we have handed off ownership of the shared struct
+ }
+ else
+ {
+ share.Release();
+ PulseAllHelper(pNewThread);
+ pNewThread->HandleThreadStartupFailure();
+ }
+ }
+ EX_CATCH
+ {
+ gc.pThis->LeaveObjMonitor();
+ EX_RETHROW;
+ }
+ EX_END_CATCH_UNREACHABLE;
+
+ gc.pThis->LeaveObjMonitor();
+
+ GCPROTECT_END();
+}
+
+FCIMPL1(void, ThreadNative::Abort, ThreadBaseObject* pThis)
+{
+ FCALL_CONTRACT;
+
+ if (pThis == NULL)
+ FCThrowVoid(kNullReferenceException);
+
+ THREADBASEREF thisRef(pThis);
+ // We need to keep the managed Thread object alive so that we can call UserAbort on
+ // unmanaged thread object.
+ HELPER_METHOD_FRAME_BEGIN_1(thisRef);
+
+ Thread *thread = thisRef->GetInternal();
+ if (thread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+#ifdef _DEBUG
+ DWORD testAbort = g_pConfig->GetHostTestThreadAbort();
+ if (testAbort != 0) {
+ thread->UserAbort(Thread::TAR_Thread, testAbort == 1 ? EEPolicy::TA_Safe : EEPolicy::TA_Rude, INFINITE, Thread::UAC_Normal);
+ }
+ else
+#endif
+ thread->UserAbort(Thread::TAR_Thread, EEPolicy::TA_V1Compatible, INFINITE, Thread::UAC_Normal);
+
+ if (thread->CatchAtSafePoint())
+ CommonTripThread();
+ HELPER_METHOD_FRAME_END_POLL();
+}
+FCIMPLEND
+
+FCIMPL1(void, ThreadNative::ResetAbort, ThreadBaseObject* pThis)
+{
+ FCALL_CONTRACT;
+
+ _ASSERTE(pThis);
+ VALIDATEOBJECT(pThis);
+
+ Thread *thread = pThis->GetInternal();
+ // We do not allow user to reset rude thread abort in MustRun code.
+ if (thread && thread->IsRudeAbort())
+ {
+ return;
+ }
+
+ HELPER_METHOD_FRAME_BEGIN_NOPOLL();
+
+ if (thread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+ thread->UserResetAbort(Thread::TAR_Thread);
+ thread->ClearAborted();
+ HELPER_METHOD_FRAME_END_POLL();
+}
+FCIMPLEND
+
+#ifndef FEATURE_CORECLR
+// You can only suspend a running thread.
+FCIMPL1(void, ThreadNative::Suspend, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE == NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ Thread *thread = pThisUNSAFE->GetInternal();
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+#ifdef MDA_SUPPORTED
+ MDA_TRIGGER_ASSISTANT(DangerousThreadingAPI, ReportViolation(W("System.Threading.Thread.Suspend")));
+#endif
+
+ if (!ThreadIsRunning(thread))
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_SUSPEND_NON_RUNNING);
+
+ thread->UserSuspendThread();
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+// You can only resume a thread that is in the user-suspended state. (This puts a large
+// burden on the app developer, but we want him to be thinking carefully about race
+// conditions. Precise errors give him a hope of sorting out his logic).
+FCIMPL1(void, ThreadNative::Resume, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE == NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ Thread *thread = pThisUNSAFE->GetInternal();
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ // UserResumeThread() will return 0 if there isn't a user suspension for us to
+ // clear.
+ if (!ThreadIsRunning(thread))
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_RESUME_NON_RUNNING);
+
+ if (thread->UserResumeThread() == 0)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_RESUME_NON_USER_SUSPEND);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#endif // FEATURE_CORECLR
+
+// Note that you can manipulate the priority of a thread that hasn't started yet,
+// or one that is running. But you get an exception if you manipulate the priority
+// of a thread that has died.
+FCIMPL1(INT32, ThreadNative::GetPriority, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ // validate the handle
+ if (ThreadIsDead(pThisUNSAFE->GetInternal()))
+ FCThrowEx(kThreadStateException, IDS_EE_THREAD_DEAD_PRIORITY, NULL, NULL, NULL);
+
+ return pThisUNSAFE->m_Priority;
+}
+FCIMPLEND
+
+FCIMPL2(void, ThreadNative::SetPriority, ThreadBaseObject* pThisUNSAFE, INT32 iPriority)
+{
+ FCALL_CONTRACT;
+
+ int priority;
+ Thread *thread;
+
+ THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_1(pThis);
+
+ if (pThis==NULL)
+ {
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+ }
+
+ // translate the priority (validating as well)
+ priority = MapToNTPriority(iPriority); // can throw; needs a frame
+
+ // validate the thread
+ thread = pThis->GetInternal();
+
+ if (ThreadIsDead(thread))
+ {
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_DEAD_PRIORITY, NULL, NULL, NULL);
+ }
+
+ INT32 oldPriority = pThis->m_Priority;
+
+ // Eliminate the race condition by establishing m_Priority before we check for if
+ // the thread is running. See ThreadNative::Start() for the other half.
+ pThis->m_Priority = iPriority;
+
+ if (!thread->SetThreadPriority(priority))
+ {
+ pThis->m_Priority = oldPriority;
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_PRIORITY_FAIL, NULL, NULL, NULL);
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+// This service can be called on unstarted and dead threads. For unstarted ones, the
+// next wait will be interrupted. For dead ones, this service quietly does nothing.
+FCIMPL1(void, ThreadNative::Interrupt, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ Thread *thread = pThisUNSAFE->GetInternal();
+
+ if (thread == 0)
+ FCThrowExVoid(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL);
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ thread->UserInterrupt(Thread::TI_Interrupt);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL1(FC_BOOL_RET, ThreadNative::IsAlive, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ THREADBASEREF thisRef(pThisUNSAFE);
+ BOOL ret = false;
+
+ // Keep managed Thread object alive, since the native object's
+ // lifetime is tied to the managed object's finalizer. And with
+ // resurrection, it may be possible to get a dangling pointer here -
+ // consider both protecting thisRef and setting the managed object's
+ // Thread* to NULL in the GC's ScanForFinalization method.
+ HELPER_METHOD_FRAME_BEGIN_RET_1(thisRef);
+
+ Thread *thread = thisRef->GetInternal();
+
+ if (thread == 0)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ ret = ThreadIsRunning(thread);
+
+ HELPER_METHOD_POLL();
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+FCIMPL2(FC_BOOL_RET, ThreadNative::Join, ThreadBaseObject* pThisUNSAFE, INT32 Timeout)
+{
+ FCALL_CONTRACT;
+
+ BOOL retVal = FALSE;
+ THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
+
+ if (pThis==NULL)
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+
+ // validate the timeout
+ if ((Timeout < 0) && (Timeout != INFINITE_TIMEOUT))
+ COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+
+ retVal = DoJoin(pThis, Timeout);
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(retVal);
+}
+FCIMPLEND
+
+#undef Sleep
+FCIMPL1(void, ThreadNative::Sleep, INT32 iTime)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ // validate the sleep time
+ if ((iTime < 0) && (iTime != INFINITE_TIMEOUT))
+ COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+
+ while(true)
+ {
+ INT64 sPauseTime = g_PauseTime;
+ INT64 sTime = CLRGetTickCount64();
+ GetThread()->UserSleep(iTime);
+ iTime = (INT32)AdditionalWait(sPauseTime, sTime, iTime);
+ if(iTime == 0)
+ break;
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#define Sleep(dwMilliseconds) Dont_Use_Sleep(dwMilliseconds)
+
+FCIMPL1(INT32, ThreadNative::GetManagedThreadId, ThreadBaseObject* th) {
+ FCALL_CONTRACT;
+
+ FC_GC_POLL_NOT_NEEDED();
+ if (th == NULL)
+ FCThrow(kNullReferenceException);
+
+ return th->GetManagedThreadId();
+}
+FCIMPLEND
+
+NOINLINE static Object* GetCurrentThreadHelper()
+{
+ FCALL_CONTRACT;
+ FC_INNER_PROLOG(ThreadNative::GetCurrentThread);
+ OBJECTREF refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refRetVal);
+ refRetVal = GetThread()->GetExposedObject();
+ HELPER_METHOD_FRAME_END();
+
+ FC_INNER_EPILOG();
+ return OBJECTREFToObject(refRetVal);
+}
+
+FCIMPL0(Object*, ThreadNative::GetCurrentThread)
+{
+ FCALL_CONTRACT;
+ OBJECTHANDLE ExposedObject = GetThread()->m_ExposedObject;
+ _ASSERTE(ExposedObject != 0); //Thread's constructor always initializes its GCHandle
+ Object* result = *((Object**) ExposedObject);
+ if (result != 0)
+ return result;
+
+ FC_INNER_RETURN(Object*, GetCurrentThreadHelper());
+}
+FCIMPLEND
+
+
+FCIMPL3(void, ThreadNative::SetStart, ThreadBaseObject* pThisUNSAFE, Object* pDelegateUNSAFE, INT32 iRequestedStackSize)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
+ OBJECTREF pDelegate = (OBJECTREF ) pDelegateUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_2(pThis, pDelegate);
+
+ _ASSERTE(pThis != NULL);
+ _ASSERTE(pDelegate != NULL); // Thread's constructor validates this
+
+ if (pThis->m_InternalThread == NULL)
+ {
+ // if we don't have an internal Thread object associated with this exposed object,
+ // now is our first opportunity to create one.
+ Thread *unstarted = SetupUnstartedThread();
+
+ PREFIX_ASSUME(unstarted != NULL);
+
+ if (GetThread()->GetDomain()->IgnoreUnhandledExceptions())
+ {
+ unstarted->SetThreadStateNC(Thread::TSNC_IgnoreUnhandledExceptions);
+ }
+
+ pThis->SetInternal(unstarted);
+ pThis->SetManagedThreadId(unstarted->GetThreadId());
+ unstarted->SetExposedObject(pThis);
+ unstarted->RequestedThreadStackSize(iRequestedStackSize);
+ }
+
+ // save off the delegate
+ pThis->SetDelegate(pDelegate);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+// Set whether or not this is a background thread.
+FCIMPL2(void, ThreadNative::SetBackground, ThreadBaseObject* pThisUNSAFE, CLR_BOOL isBackground)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ // validate the thread
+ Thread *thread = pThisUNSAFE->GetInternal();
+
+ if (ThreadIsDead(thread))
+ FCThrowExVoid(kThreadStateException, IDS_EE_THREAD_DEAD_STATE, NULL, NULL, NULL);
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ thread->SetBackground(isBackground);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+// Return whether or not this is a background thread.
+FCIMPL1(FC_BOOL_RET, ThreadNative::IsBackground, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ // validate the thread
+ Thread *thread = pThisUNSAFE->GetInternal();
+
+ if (ThreadIsDead(thread))
+ FCThrowEx(kThreadStateException, IDS_EE_THREAD_DEAD_STATE, NULL, NULL, NULL);
+
+ FC_RETURN_BOOL(thread->IsBackground());
+}
+FCIMPLEND
+
+
+// Deliver the state of the thread as a consistent set of bits.
+// This copied in VM\EEDbgInterfaceImpl.h's
+// CorDebugUserState GetUserState( Thread *pThread )
+// , so propogate changes to both functions
+FCIMPL1(INT32, ThreadNative::GetThreadState, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ INT32 res = 0;
+ Thread::ThreadState state;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ // validate the thread. Failure here implies that the thread was finalized
+ // and then resurrected.
+ Thread *thread = pThisUNSAFE->GetInternal();
+
+ if (!thread)
+ FCThrowEx(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ // grab a snapshot
+ state = thread->GetSnapshotState();
+
+ if (state & Thread::TS_Background)
+ res |= ThreadBackground;
+
+ if (state & Thread::TS_Unstarted)
+ res |= ThreadUnstarted;
+
+ // Don't report a StopRequested if the thread has actually stopped.
+ if (state & Thread::TS_Dead)
+ {
+ if (state & Thread::TS_Aborted)
+ res |= ThreadAborted;
+ else
+ res |= ThreadStopped;
+ }
+ else
+ {
+ if (state & Thread::TS_AbortRequested)
+ res |= ThreadAbortRequested;
+ }
+
+ if (state & Thread::TS_Interruptible)
+ res |= ThreadWaitSleepJoin;
+
+ // Don't report a SuspendRequested if the thread has actually Suspended.
+ if ((state & Thread::TS_UserSuspendPending) &&
+ (state & Thread::TS_SyncSuspended)
+ )
+ {
+ res |= ThreadSuspended;
+ }
+ else
+ if (state & Thread::TS_UserSuspendPending)
+ {
+ res |= ThreadSuspendRequested;
+ }
+
+ HELPER_METHOD_POLL();
+ HELPER_METHOD_FRAME_END();
+
+ return res;
+}
+FCIMPLEND
+
+#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
+
+// Indicate whether the thread will host an STA (this may fail if the thread has
+// already been made part of the MTA, use GetApartmentState or the return state
+// from this routine to check for this).
+FCIMPL3(INT32, ThreadNative::SetApartmentState, ThreadBaseObject* pThisUNSAFE, INT32 iState, CLR_BOOL fireMDAOnMismatch)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ INT32 retVal = ApartmentUnknown;
+ BOOL ok = TRUE;
+ THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
+
+ // Translate state input. ApartmentUnknown is not an acceptable input state.
+ // Throw an exception here rather than pass it through to the internal
+ // routine, which asserts.
+ Thread::ApartmentState state = Thread::AS_Unknown;
+ if (iState == ApartmentSTA)
+ state = Thread::AS_InSTA;
+ else if (iState == ApartmentMTA)
+ state = Thread::AS_InMTA;
+ else if (iState == ApartmentUnknown)
+ state = Thread::AS_Unknown;
+ else
+ COMPlusThrow(kArgumentOutOfRangeException, W("ArgumentOutOfRange_Enum"));
+
+ Thread *thread = pThis->GetInternal();
+ if (!thread)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ {
+ pThis->EnterObjMonitor();
+
+ // We can only change the apartment if the thread is unstarted or
+ // running, and if it's running we have to be in the thread's
+ // context.
+ if ((!ThreadNotStarted(thread) && !ThreadIsRunning(thread)) ||
+ (!ThreadNotStarted(thread) && (GetThread() != thread)))
+ ok = FALSE;
+ else
+ {
+ EX_TRY
+ {
+ state = thread->SetApartment(state, fireMDAOnMismatch == TRUE);
+ }
+ EX_CATCH
+ {
+ pThis->LeaveObjMonitor();
+ EX_RETHROW;
+ }
+ EX_END_CATCH_UNREACHABLE;
+ }
+
+ pThis->LeaveObjMonitor();
+ }
+
+
+ // Now it's safe to throw exceptions again.
+ if (!ok)
+ COMPlusThrow(kThreadStateException);
+
+ // Translate state back into external form
+ if (state == Thread::AS_InSTA)
+ retVal = ApartmentSTA;
+ else if (state == Thread::AS_InMTA)
+ retVal = ApartmentMTA;
+ else if (state == Thread::AS_Unknown)
+ retVal = ApartmentUnknown;
+ else
+ _ASSERTE(!"Invalid state returned from SetApartment");
+
+ HELPER_METHOD_FRAME_END();
+
+ return retVal;
+}
+FCIMPLEND
+
+// Return whether the thread hosts an STA, is a member of the MTA or is not
+// currently initialized for COM.
+FCIMPL1(INT32, ThreadNative::GetApartmentState, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ INT32 retVal = 0;
+
+ THREADBASEREF refThis = (THREADBASEREF) ObjectToOBJECTREF(pThisUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refThis);
+
+ if (refThis == NULL)
+ {
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+ }
+
+ Thread* thread = refThis->GetInternal();
+
+ if (ThreadIsDead(thread))
+ {
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_DEAD_STATE);
+ }
+
+ Thread::ApartmentState state = thread->GetApartment();
+
+#ifdef FEATURE_COMINTEROP
+ if (state == Thread::AS_Unknown)
+ {
+ // If the CLR hasn't started COM yet, start it up and attempt the call again.
+ // We do this in order to minimize the number of situations under which we return
+ // ApartmentState.Unknown to our callers.
+ if (!g_fComStarted)
+ {
+ EnsureComStarted();
+ state = thread->GetApartment();
+ }
+ }
+#endif // FEATURE_COMINTEROP
+
+ // Translate state into external form
+ retVal = ApartmentUnknown;
+ if (state == Thread::AS_InSTA)
+ {
+ retVal = ApartmentSTA;
+ }
+ else if (state == Thread::AS_InMTA)
+ {
+ retVal = ApartmentMTA;
+ }
+ else if (state == Thread::AS_Unknown)
+ {
+ retVal = ApartmentUnknown;
+ }
+ else
+ {
+ _ASSERTE(!"Invalid state returned from GetApartment");
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return retVal;
+}
+FCIMPLEND
+
+
+// Attempt to eagerly set the apartment state during thread startup.
+FCIMPL1(void, ThreadNative::StartupSetApartmentState, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ THREADBASEREF refThis = (THREADBASEREF) ObjectToOBJECTREF(pThisUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_1(refThis);
+
+ if (refThis == NULL)
+ {
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+ }
+
+ Thread* thread = refThis->GetInternal();
+
+ if (!ThreadNotStarted(thread))
+ COMPlusThrow(kThreadStateException, IDS_EE_THREADSTART_STATE);
+
+ // Assert that the thread hasn't been started yet.
+ _ASSERTE(Thread::TS_Unstarted & thread->GetSnapshotState());
+
+ if ((g_pConfig != NULL) && !g_pConfig->LegacyApartmentInitPolicy())
+ {
+ Thread::ApartmentState as = thread->GetExplicitApartment();
+ if (as == Thread::AS_Unknown)
+ {
+ thread->SetApartment(Thread::AS_InMTA, TRUE);
+ }
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
+
+void ReleaseThreadExternalCount(Thread * pThread)
+{
+ WRAPPER_NO_CONTRACT;
+ pThread->DecExternalCount(FALSE);
+}
+
+typedef Holder<Thread *, DoNothing, ReleaseThreadExternalCount> ThreadExternalCountHolder;
+
+// Wait for the thread to die
+BOOL ThreadNative::DoJoin(THREADBASEREF DyingThread, INT32 timeout)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(DyingThread != NULL);
+ PRECONDITION((timeout >= 0) || (timeout == INFINITE_TIMEOUT));
+ }
+ CONTRACTL_END;
+
+ Thread * DyingInternal = DyingThread->GetInternal();
+
+ // Validate the handle. It's valid to Join a thread that's not running -- so
+ // long as it was once started.
+ if (DyingInternal == 0 ||
+ !(DyingInternal->m_State & Thread::TS_LegalToJoin))
+ {
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_NOTSTARTED);
+ }
+
+ // Don't grab the handle until we know it has started, to eliminate the race
+ // condition.
+ if (ThreadIsDead(DyingInternal) || !DyingInternal->HasValidThreadHandle())
+ return TRUE;
+
+ DWORD dwTimeOut32 = (timeout == INFINITE_TIMEOUT
+ ? INFINITE
+ : (DWORD) timeout);
+
+ // There is a race here. DyingThread is going to close its thread handle.
+ // If we grab the handle and then DyingThread closes it, we will wait forever
+ // in DoAppropriateWait.
+ int RefCount = DyingInternal->IncExternalCount();
+ if (RefCount == 1)
+ {
+ // !!! We resurrect the Thread Object.
+ // !!! We will keep the Thread ref count to be 1 so that we will not try
+ // !!! to destroy the Thread Object again.
+ // !!! Do not call DecExternalCount here!
+ _ASSERTE (!DyingInternal->HasValidThreadHandle());
+ return TRUE;
+ }
+
+ ThreadExternalCountHolder dyingInternalHolder(DyingInternal);
+
+ if (!DyingInternal->HasValidThreadHandle())
+ {
+ return TRUE;
+ }
+
+ GCX_PREEMP();
+ DWORD rv = DyingInternal->JoinEx(dwTimeOut32, (WaitMode)(WaitMode_Alertable/*alertable*/|WaitMode_InDeadlock));
+
+ switch(rv)
+ {
+ case WAIT_OBJECT_0:
+ return TRUE;
+
+ case WAIT_TIMEOUT:
+ break;
+
+ case WAIT_FAILED:
+ if(!DyingInternal->HasValidThreadHandle())
+ return TRUE;
+ break;
+
+ default:
+ _ASSERTE(!"This return code is not understood \n");
+ break;
+ }
+
+ return FALSE;
+}
+
+
+// We don't get a constructor for ThreadBaseObject, so we rely on the fact that this
+// method is only called once, out of SetStart. Since SetStart is private/native
+// and only called from the constructor, we'll only get called here once to set it
+// up and once (with NULL) to tear it down. The 'null' can only come from Finalize
+// because the constructor throws if it doesn't get a valid delegate.
+void ThreadBaseObject::SetDelegate(OBJECTREF delegate)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+#ifdef APPDOMAIN_STATE
+ if (delegate != NULL)
+ {
+ AppDomain *pDomain = delegate->GetAppDomain();
+ Thread *pThread = GetInternal();
+ AppDomain *kickoffDomain = pThread->GetKickOffDomain();
+ _ASSERTE_ALL_BUILDS("clr/src/VM/COMSynchronizable.cpp", !pDomain || pDomain == kickoffDomain);
+ _ASSERTE_ALL_BUILDS("clr/src/VM/COMSynchronizable.cpp", kickoffDomain == GetThread()->GetDomain());
+ }
+#endif
+
+ SetObjectReferenceUnchecked( (OBJECTREF *)&m_Delegate, delegate );
+
+ // If the delegate is being set then initialize the other data members.
+ if (m_Delegate != NULL)
+ {
+ // Initialize the thread priority to normal.
+ m_Priority = ThreadNative::PRIORITY_NORMAL;
+ }
+}
+
+
+// If the exposed object is created after-the-fact, for an existing thread, we call
+// InitExisting on it. This is the other "construction", as opposed to SetDelegate.
+void ThreadBaseObject::InitExisting()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ Thread *pThread = GetInternal();
+ _ASSERTE (pThread);
+ switch (pThread->GetThreadPriority())
+ {
+ case THREAD_PRIORITY_LOWEST:
+ case THREAD_PRIORITY_IDLE:
+ m_Priority = ThreadNative::PRIORITY_LOWEST;
+ break;
+
+ case THREAD_PRIORITY_BELOW_NORMAL:
+ m_Priority = ThreadNative::PRIORITY_BELOW_NORMAL;
+ break;
+
+ case THREAD_PRIORITY_NORMAL:
+ m_Priority = ThreadNative::PRIORITY_NORMAL;
+ break;
+
+ case THREAD_PRIORITY_ABOVE_NORMAL:
+ m_Priority = ThreadNative::PRIORITY_ABOVE_NORMAL;
+ break;
+
+ case THREAD_PRIORITY_HIGHEST:
+ case THREAD_PRIORITY_TIME_CRITICAL:
+ m_Priority = ThreadNative::PRIORITY_HIGHEST;
+ break;
+
+ case THREAD_PRIORITY_ERROR_RETURN:
+ _ASSERTE(FALSE);
+ m_Priority = ThreadNative::PRIORITY_NORMAL;
+ break;
+
+ default:
+ m_Priority = ThreadNative::PRIORITY_NORMAL;
+ break;
+ }
+
+}
+
+#ifndef FEATURE_LEAK_CULTURE_INFO
+OBJECTREF ThreadBaseObject::GetManagedThreadCulture(BOOL bUICulture)
+{
+ CONTRACTL {
+ GC_NOTRIGGER;
+ NOTHROW;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ // This is the case when we're building mscorlib and haven't yet created
+ // the system assembly.
+ if (SystemDomain::System()->SystemAssembly()==NULL || g_fForbidEnterEE) {
+ return NULL;
+ }
+
+ OBJECTREF *pCurrentCulture = NULL;
+ Thread *pThread = GetInternal();
+ FieldDesc *pFD = NULL;
+
+ if (bUICulture)
+ {
+ pFD = pThread->managedThreadCurrentUICulture;
+ }
+ else
+ {
+ pFD = pThread->managedThreadCurrentCulture;
+ }
+
+ if (pFD != NULL)
+ {
+ pCurrentCulture = (OBJECTREF*)pThread->GetStaticFieldAddrNoCreate(pFD, NULL);
+ if (pCurrentCulture)
+ {
+ return *pCurrentCulture;
+ }
+ }
+
+ return NULL;
+}
+
+CULTUREINFOBASEREF ThreadBaseObject::GetCurrentUserCulture()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (CULTUREINFOBASEREF)GetManagedThreadCulture(false);
+}
+
+CULTUREINFOBASEREF ThreadBaseObject::GetCurrentUICulture()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (CULTUREINFOBASEREF)GetManagedThreadCulture(true);
+}
+
+// If the thread pool thread switched appdomains and the culture was set, the culture won't be
+// reset for the second appdomain. It's impossible to do general cleanup of thread pool threads
+// because we don't have the right extensible infrastructure for it. For example, if the second
+// appdomain was in a different CLR you won't be able to reset the culture without introducing
+// new cross-CLR communication mechanism. However, note that this isn't a user scenario in
+// CoreCLR anyway.
+void ThreadBaseObject::ResetCurrentUserCulture()
+{
+ WRAPPER_NO_CONTRACT;
+ ResetManagedThreadCulture(false);
+}
+
+void ThreadBaseObject::ResetCurrentUICulture()
+{
+ WRAPPER_NO_CONTRACT;
+ ResetManagedThreadCulture(true);
+}
+
+void ThreadBaseObject::ResetManagedThreadCulture(BOOL bUICulture)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ // This is the case when we're building mscorlib and haven't yet created
+ // the system assembly.
+ if (SystemDomain::System()->SystemAssembly()==NULL || g_fForbidEnterEE) {
+ return;
+ }
+
+ Thread *pThread = GetInternal();
+ FieldDesc *pFD = NULL;
+
+ if (bUICulture)
+ {
+ pFD = pThread->managedThreadCurrentUICulture;
+ }
+ else
+ {
+ pFD = pThread->managedThreadCurrentCulture;
+ }
+
+ if (pFD != NULL)
+ {
+ OBJECTREF *pCulture = NULL;
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO());
+ pCulture = (OBJECTREF*)pThread->GetStaticFieldAddrNoCreate(pFD, NULL);
+ if (pCulture)
+ {
+ SetObjectReferenceUnchecked(pCulture, NULL);
+ }
+ END_SO_INTOLERANT_CODE;
+
+ }
+}
+
+#endif // FEATURE_LEAK_CULTURE_INFO
+
+
+FCIMPL1(void, ThreadNative::Finalize, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ // This function is intentionally blank.
+ // See comment in code:MethodTable::CallFinalizer.
+
+ _ASSERTE (!"Should not be called");
+
+ FCUnique(0x21);
+}
+FCIMPLEND
+
+#ifdef FEATURE_COMINTEROP
+FCIMPL1(void, ThreadNative::DisableComObjectEagerCleanup, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ _ASSERTE(pThisUNSAFE != NULL);
+ VALIDATEOBJECT(pThisUNSAFE);
+ Thread *pThread = pThisUNSAFE->GetInternal();
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ pThread->SetDisableComObjectEagerCleanup();
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+#endif //FEATURE_COMINTEROP
+
+#ifdef FEATURE_LEAK_CULTURE_INFO
+FCIMPL1(FC_BOOL_RET, ThreadNative::SetThreadUILocale, StringObject* localeNameUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ BOOL result = TRUE;
+
+ STRINGREF name = (STRINGREF) localeNameUNSAFE;
+ VALIDATEOBJECTREF(name);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ LCID lcid=NewApis::LocaleNameToLCID(name->GetBuffer(),0);
+ if (lcid == 0)
+ {
+ ThrowHR(HRESULT_FROM_WIN32(GetLastError()));
+ }
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostTaskManager *manager = CorHost2::GetHostTaskManager();
+ if (manager) {
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ result = (manager->SetUILocale(lcid) == S_OK);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(result);
+}
+FCIMPLEND
+#endif // FEATURE_LEAK_CULTURE_INFO
+
+FCIMPL0(Object*, ThreadNative::GetDomain)
+{
+ FCALL_CONTRACT;
+
+ APPDOMAINREF refRetVal = NULL;
+
+ Thread* thread = GetThread();
+
+ if ((thread) && (thread->GetDomain()))
+ {
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+ refRetVal = (APPDOMAINREF) thread->GetDomain()->GetExposedObject();
+ HELPER_METHOD_FRAME_END();
+ }
+
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+#ifdef _TARGET_X86_
+__declspec(naked) LPVOID __fastcall ThreadNative::FastGetDomain()
+{
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ __asm {
+ call GetAppDomain
+ test eax, eax
+ je done
+ mov eax, dword ptr [eax]AppDomain.m_ExposedObject
+ test eax, eax
+ je done
+ mov eax, dword ptr [eax]
+done:
+ ret
+ }
+}
+#else // _TARGET_X86_
+LPVOID F_CALL_CONV ThreadNative::FastGetDomain()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ Thread *pThread;
+ AppDomain *pDomain;
+ OBJECTHANDLE ExposedObject;
+
+ pDomain = GetAppDomain();
+ if (!pDomain) {
+ return NULL;
+ }
+ ExposedObject = pDomain->m_ExposedObject;
+ if (ExposedObject) {
+ return *(LPVOID *)ExposedObject;
+ }
+ return NULL;
+}
+#endif // _TARGET_X86_
+
+#ifdef FEATURE_REMOTING
+// This is just a helper method that lets BCL get to the managed context
+// from the contextID.
+FCIMPL1(Object*, ThreadNative::GetContextFromContextID, LPVOID ContextID)
+{
+ FCALL_CONTRACT;
+
+ OBJECTREF rv = NULL;
+ Context* pCtx = (Context *) ContextID;
+ // Get the managed context backing this unmanaged context
+ rv = pCtx->GetExposedObjectRaw();
+
+ // This assert maintains the following invariant:
+ // Only default unmanaged contexts can have a null managed context
+ // (All non-deafult contexts are created as managed contexts first, and then
+ // hooked to the unmanaged context)
+ _ASSERTE((rv != NULL) || (pCtx->GetDomain()->GetDefaultContext() == pCtx));
+
+ return OBJECTREFToObject(rv);
+}
+FCIMPLEND
+
+
+FCIMPL6(Object*, ThreadNative::InternalCrossContextCallback, ThreadBaseObject* refThis, ContextBaseObject* refContext, LPVOID contextID, INT32 appDomainId, Object* oDelegateUNSAFE, PtrArray* oArgsUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ _ASSERTE(refThis != NULL);
+ VALIDATEOBJECT(refThis);
+ Thread *pThread = refThis->GetInternal();
+ Context *pCtx = (Context *)contextID;
+
+
+ _ASSERTE(pCtx && (refContext == NULL || pCtx->GetExposedObjectRaw() == NULL ||
+ ObjectToOBJECTREF(refContext) == pCtx->GetExposedObjectRaw()));
+ LOG((LF_APPDOMAIN, LL_INFO1000, "ThreadNative::InternalCrossContextCallback: %p, %p\n", refContext, pCtx));
+ // install our frame. We have to put it here before we put the helper frame on
+
+ // Set the VM conext
+
+ struct _gc {
+ OBJECTREF oRetVal;
+ OBJECTREF oDelegate;
+ OBJECTREF oArgs;
+ // We need to report the managed context object because it may become unreachable in the caller,
+ // however we have to keep it alive, otherwise its finalizer could free the unmanaged internal context
+ OBJECTREF oContext;
+ } gc;
+
+ gc.oRetVal = NULL;
+ gc.oDelegate = ObjectToOBJECTREF(oDelegateUNSAFE);
+ gc.oArgs = ObjectToOBJECTREF(oArgsUNSAFE);
+ gc.oContext = ObjectToOBJECTREF(refContext);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+#ifdef _DEBUG
+ MethodDesc* pTargetMD = COMDelegate::GetMethodDesc(gc.oDelegate);
+ _ASSERTE(pTargetMD->IsStatic());
+#endif
+
+ // If we have a non-zero appDomain index, this is a x-domain call
+ // We must verify that the AppDomain is not unloaded
+ PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__COMPLETE_CROSSCONTEXTCALLBACK);
+
+ AppDomainFromIDHolder ad;
+ if (appDomainId != 0)
+ {
+ //
+ // NOTE: there is a potential race between the time we retrieve the app domain pointer,
+ // and the time which this thread enters the domain.
+ //
+ // To solve the race, we rely on the fact that there is a thread sync
+ // between releasing an app domain's handle, and destroying the app domain. Thus
+ // it is important that we not go into preemptive gc mode in that window.
+ //
+ {
+ ad.Assign(ADID(appDomainId), TRUE);
+
+ if (ad.IsUnloaded() || !ad->CanThreadEnter(pThread))
+ COMPlusThrow(kAppDomainUnloadedException, W("Remoting_AppDomainUnloaded"));
+ }
+ }
+
+ // Verify that the Context is valid.
+ if ( !Context::ValidateContext(pCtx) )
+ COMPlusThrow(kRemotingException, W("Remoting_InvalidContext"));
+
+ DEBUG_ASSURE_NO_RETURN_BEGIN(COMSYNCH)
+
+ FrameWithCookie<ContextTransitionFrame> frame;
+
+ Context* pCurrContext = pThread->GetContext();
+ bool fTransition = (pCurrContext != pCtx);
+ BOOL fSameDomain = (appDomainId==0) || (pCurrContext->GetDomain()->GetId() == (ADID)appDomainId);
+ _ASSERTE( fTransition || fSameDomain);
+ if (fTransition)
+ if (appDomainId!=0)
+ ad->EnterContext(pThread,pCtx, &frame);
+ else
+ pThread->EnterContextRestricted(pCtx,&frame);
+ ad.Release();
+
+
+ LOG((LF_EH, LL_INFO100, "MSCORLIB_ENTER_CONTEXT( %s::%s ): %s\n",
+ pTargetMD->m_pszDebugClassName,
+ pTargetMD->m_pszDebugMethodName,
+ fTransition ? "ENTERED" : "NOP"));
+
+ Exception* pOriginalException=NULL;
+
+ EX_TRY
+ {
+ DECLARE_ARGHOLDER_ARRAY(callArgs, 2);
+
+#if CHECK_APP_DOMAIN_LEAKS
+ // We're passing the delegate object to another appdomain
+ // without marshaling, that is OK - it's a static function delegate
+ // but we should mark it as agile then.
+ gc.oDelegate->SetSyncBlockAppDomainAgile();
+#endif
+ callArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.oDelegate);
+ callArgs[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.oArgs);
+
+ CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE;
+ CALL_MANAGED_METHOD_RETREF(gc.oRetVal, OBJECTREF, callArgs);
+ }
+ EX_CATCH
+ {
+ LOG((LF_EH, LL_INFO100, "MSCORLIB_CONTEXT_TRANSITION( %s::%s ): exception in flight\n", pTargetMD->m_pszDebugClassName, pTargetMD->m_pszDebugMethodName));
+
+ if (!fTransition || fSameDomain)
+ {
+ if (fTransition)
+ {
+ GCX_FORBID();
+ pThread->ReturnToContext(&frame);
+ }
+#ifdef FEATURE_TESTHOOKS
+ if (appDomainId!=0)
+ {
+ TESTHOOKCALL(LeftAppDomain(appDomainId));
+ }
+#endif
+ EX_RETHROW;
+ }
+
+ pOriginalException=EXTRACT_EXCEPTION();
+ CAPTURE_BUCKETS_AT_TRANSITION(pThread, CLRException::GetThrowableFromException(pOriginalException));
+ goto lAfterCtxUnwind;
+ }
+ EX_END_CATCH_UNREACHABLE;
+ if (0)
+ {
+lAfterCtxUnwind:
+ LOG((LF_EH, LL_INFO100, "MSCORLIB_RaiseCrossContextException( %s::%s )\n", pTargetMD->m_pszDebugClassName, pTargetMD->m_pszDebugMethodName));
+ pThread->RaiseCrossContextException(pOriginalException,&frame);
+ }
+
+ LOG((LF_EH, LL_INFO100, "MSCORLIB_LEAVE_CONTEXT_TRANSITION( %s::%s )\n", pTargetMD->m_pszDebugClassName, pTargetMD->m_pszDebugMethodName));
+
+ if (fTransition)
+ {
+ GCX_FORBID();
+ pThread->ReturnToContext(&frame);
+ }
+#ifdef FEATURE_TESTHOOKS
+ if(appDomainId!=0)
+ {
+ TESTHOOKCALL(LeftAppDomain(appDomainId));
+ }
+#endif
+
+ DEBUG_ASSURE_NO_RETURN_END(COMSYNCH)
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(gc.oRetVal);
+}
+FCIMPLEND
+#endif //FEATURE_REMOTING
+
+//
+// nativeGetSafeCulture is used when the culture get requested from the thread object.
+// we have to check the culture in the FCALL because in FCALL the thread cannot be
+// interrupted and unload other app domian.
+// the concern here is if the thread hold a subclassed culture object and somebody
+// requested it from other app domain then we shouldn't hold any reference to that
+// culture object any time because the app domain created this culture may get
+// unloaded and this culture will survive although the type metadata will be unloaded
+// and GC will crash first time accessing this object after the app domain unload.
+//
+#ifdef FEATURE_LEAK_CULTURE_INFO
+FCIMPL4(FC_BOOL_RET, ThreadNative::nativeGetSafeCulture,
+ ThreadBaseObject* threadUNSAFE,
+ int appDomainId,
+ CLR_BOOL isUI,
+ OBJECTREF* safeCulture)
+{
+ FCALL_CONTRACT;
+
+ THREADBASEREF thread(threadUNSAFE);
+
+ CULTUREINFOBASEREF pCulture = isUI ? thread->GetCurrentUICulture() : thread->GetCurrentUserCulture();
+ if (pCulture != NULL) {
+ if (pCulture->IsSafeCrossDomain() || pCulture->GetCreatedDomainID() == ADID(appDomainId)) {
+ SetObjectReference(safeCulture, pCulture, pCulture->GetAppDomain());
+ } else {
+ FC_RETURN_BOOL(FALSE);
+ }
+ }
+ FC_RETURN_BOOL(TRUE);
+}
+FCIMPLEND
+#endif // FEATURE_LEAK_CULTURE_INFO
+
+#ifndef FEATURE_LEAK_CULTURE_INFO
+void QCALLTYPE ThreadNative::nativeInitCultureAccessors()
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ Thread* pThread = GetThread();
+ pThread->InitCultureAccessors();
+
+ END_QCALL;
+}
+#endif // FEATURE_LEAK_CULTURE_INFO
+
+
+void QCALLTYPE ThreadNative::InformThreadNameChange(QCall::ThreadHandle thread, LPCWSTR name, INT32 len)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ Thread* pThread = &(*thread);
+
+#ifdef PROFILING_SUPPORTED
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackThreads());
+ if (name == NULL)
+ {
+ g_profControlBlock.pProfInterface->ThreadNameChanged((ThreadID)pThread, 0, NULL);
+ }
+ else
+ {
+ g_profControlBlock.pProfInterface->ThreadNameChanged((ThreadID)pThread, len, (WCHAR*)name);
+ }
+ END_PIN_PROFILER();
+ }
+#endif // PROFILING_SUPPORTED
+
+
+#ifdef DEBUGGING_SUPPORTED
+ if (CORDebuggerAttached())
+ {
+ _ASSERTE(NULL != g_pDebugInterface);
+ g_pDebugInterface->NameChangeEvent(NULL, pThread);
+ }
+#endif // DEBUGGING_SUPPORTED
+
+ END_QCALL;
+}
+
+UINT64 QCALLTYPE ThreadNative::GetProcessDefaultStackSize()
+{
+ QCALL_CONTRACT;
+
+ SIZE_T reserve = 0;
+ SIZE_T commit = 0;
+
+ BEGIN_QCALL;
+
+ if (!Thread::GetProcessDefaultStackSize(&reserve, &commit))
+ reserve = 1024 * 1024;
+
+ END_QCALL;
+
+ return (UINT64)reserve;
+}
+
+#ifndef FEATURE_CORECLR
+FCIMPL0(void, ThreadNative::BeginCriticalRegion)
+{
+ FCALL_CONTRACT;
+ if (CLRHosted())
+ {
+ GetThread()->BeginCriticalRegion_NoCheck();
+ }
+}
+FCIMPLEND
+
+FCIMPL0(void, ThreadNative::EndCriticalRegion)
+{
+ FCALL_CONTRACT;
+ if (CLRHosted())
+ {
+ GetThread()->EndCriticalRegion_NoCheck();
+ }
+}
+FCIMPLEND
+
+FCIMPL0(void, ThreadNative::BeginThreadAffinity)
+{
+ FCALL_CONTRACT;
+ Thread::BeginThreadAffinity();
+}
+FCIMPLEND
+
+FCIMPL0(void, ThreadNative::EndThreadAffinity)
+{
+ FCALL_CONTRACT;
+ Thread::EndThreadAffinity();
+}
+FCIMPLEND
+#endif // !FEATURE_CORECLR
+
+
+FCIMPL1(FC_BOOL_RET, ThreadNative::IsThreadpoolThread, ThreadBaseObject* thread)
+{
+ FCALL_CONTRACT;
+
+ if (thread==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ Thread *pThread = thread->GetInternal();
+
+ if (pThread == NULL)
+ FCThrowEx(kThreadStateException, IDS_EE_THREAD_DEAD_STATE, NULL, NULL, NULL);
+
+ BOOL ret = pThread->IsThreadPoolThread();
+
+ FC_GC_POLL_RET();
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+
+FCIMPL1(void, ThreadNative::SpinWait, int iterations)
+{
+ FCALL_CONTRACT;
+
+ //
+ // If we're not going to spin for long, it's ok to remain in cooperative mode.
+ // The threshold is determined by the cost of entering preemptive mode; if we're
+ // spinning for less than that number of cycles, then switching to preemptive
+ // mode won't help a GC start any faster. That number is right around 1000000
+ // on my machine.
+ //
+ if (iterations <= 1000000)
+ {
+ for(int i = 0; i < iterations; i++)
+ YieldProcessor();
+ return;
+ }
+
+ //
+ // Too many iterations; better switch to preemptive mode to avoid stalling a GC.
+ //
+ HELPER_METHOD_FRAME_BEGIN_NOPOLL();
+ GCX_PREEMP();
+
+ for(int i = 0; i < iterations; i++)
+ YieldProcessor();
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+BOOL QCALLTYPE ThreadNative::YieldThread()
+{
+ QCALL_CONTRACT;
+
+ BOOL ret = FALSE;
+
+ BEGIN_QCALL
+
+ ret = __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+
+ END_QCALL
+
+ return ret;
+}
+
+#ifdef FEATURE_COMPRESSEDSTACK
+FCIMPL2(void*, ThreadNative::SetAppDomainStack, ThreadBaseObject* pThis, SafeHandle* hcsUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ void* pRet = NULL;
+ SAFEHANDLE hcsSAFE = (SAFEHANDLE) hcsUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(hcsSAFE);
+
+
+ void* unmanagedCompressedStack = NULL;
+ if (hcsSAFE != NULL)
+ {
+ unmanagedCompressedStack = (void *)hcsSAFE->GetHandle();
+ }
+
+
+ VALIDATEOBJECT(pThis);
+ Thread *pThread = pThis->GetInternal();
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ pRet = StackCompressor::SetAppDomainStack(pThread, unmanagedCompressedStack);
+ HELPER_METHOD_FRAME_END_POLL();
+ return pRet;
+}
+FCIMPLEND
+
+
+FCIMPL2(void, ThreadNative::RestoreAppDomainStack, ThreadBaseObject* pThis, void* appDomainStack)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_NOPOLL();
+
+ VALIDATEOBJECT(pThis);
+ Thread *pThread = pThis->GetInternal();
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ StackCompressor::RestoreAppDomainStack(pThread, appDomainStack);
+ HELPER_METHOD_FRAME_END_POLL();
+}
+FCIMPLEND
+#endif //#ifdef FEATURE_COMPRESSEDSTACK
+
+FCIMPL0(void, ThreadNative::FCMemoryBarrier)
+{
+ FCALL_CONTRACT;
+
+ MemoryBarrier();
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+FCIMPL2(void, ThreadNative::SetAbortReason, ThreadBaseObject* pThisUNSAFE, Object* pObject)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ OBJECTREF refObject = static_cast<OBJECTREF>(pObject);
+
+ Thread *pThread = pThisUNSAFE->GetInternal();
+
+ // If the OBJECTHANDLE is not 0, already set so just return
+ if (pThread != NULL && pThread->m_AbortReason != 0)
+ return;
+
+ // Set up a frame in case of GC or EH
+ HELPER_METHOD_FRAME_BEGIN_1(refObject)
+
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ // Get the AppDomain ID for the AppDomain on the currently running thread.
+ // NOTE: the currently running thread may be different from this thread object!
+ AppDomain *pCurrentDomain = GetThread()->GetDomain();
+ ADID adid = pCurrentDomain->GetId();
+
+ // Create a OBJECTHANDLE for the object.
+ OBJECTHANDLE oh = pCurrentDomain->CreateHandle(refObject);
+
+ // Scope the lock to peeking at and updating the two fields on the Thread object.
+ { // Atomically check whether the OBJECTHANDLE has been set, and if not,
+ // store it and the ADID of the object.
+ // NOTE: get the lock on this thread object, not on the executing thread.
+ Thread::AbortRequestLockHolder lock(pThread);
+ if (pThread->m_AbortReason == 0)
+ {
+ pThread->m_AbortReason = oh;
+ pThread->m_AbortReasonDomainID = adid;
+ // Set the OBJECTHANDLE so we can know that we stored it on the Thread object.
+ oh = 0;
+ }
+ }
+
+ // If the OBJECTHANDLE created above was not stored onto the Thread object, then
+ // another thread beat this one to the update. Destroy the OBJECTHANDLE that
+ // was not used, created above.
+ if (oh != 0)
+ {
+ DestroyHandle(oh);
+ }
+
+ HELPER_METHOD_FRAME_END()
+
+}
+FCIMPLEND
+
+#ifndef FEATURE_CORECLR // core clr does not support abort reason
+FCIMPL1(Object*, ThreadNative::GetAbortReason, ThreadBaseObject *pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowRes(kNullReferenceException, W("NullReference_This"));
+
+ OBJECTREF refRetVal = NULL;
+ Thread *pThread = pThisUNSAFE->GetInternal();
+
+ // Set up a frame in case of GC or EH
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal)
+
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ // While the ExceptionInfo probably will be *set* from a different
+ // thread, it should only be *read* from the current thread.
+ _ASSERTE(GetThread() == pThread);
+
+ // Set cooperative mode, to avoid AD unload while we're working.
+ GCX_COOP();
+
+ OBJECTHANDLE oh=NULL;
+ ADID adid;
+ // Scope the lock to reading the two fields on the Thread object.
+ { // Atomically get the OBJECTHANDLE and ADID of the object
+ // NOTE: get the lock on this thread object, not on the executing thread.
+ Thread::AbortRequestLockHolder lock(pThread);
+ oh = pThread->m_AbortReason;
+ adid = pThread->m_AbortReasonDomainID;
+ }
+
+ // If the OBJECTHANDLE is not 0...
+ if (oh != 0)
+ {
+
+ AppDomain *pCurrentDomain = pThread->GetDomain();
+ // See if the appdomain is equal to the appdomain of the currently running
+ // thread.
+
+ if (pCurrentDomain->GetId() == adid)
+ { // Same appdomain; just return object from the OBJECTHANDLE
+ refRetVal = ObjectFromHandle(oh);
+ }
+ else
+ { // Otherwise, try to marshal the object from the other AppDomain
+ ENTER_DOMAIN_ID(adid);
+ CrossAppDomainClonerCallback cadcc;
+ ObjectClone Cloner(&cadcc, CrossAppDomain, FALSE);
+ refRetVal = Cloner.Clone(ObjectFromHandle(oh), GetAppDomain(), pCurrentDomain, NULL);
+ Cloner.RemoveGCFrames();
+ END_DOMAIN_TRANSITION;
+ }
+ }
+
+ HELPER_METHOD_FRAME_END()
+
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+#endif // !FEATURE_CORECLR
+
+FCIMPL1(void, ThreadNative::ClearAbortReason, ThreadBaseObject* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ if (pThisUNSAFE==NULL)
+ FCThrowResVoid(kNullReferenceException, W("NullReference_This"));
+
+ Thread *pThread = pThisUNSAFE->GetInternal();
+
+ // Clearing from managed code can only happen on the current thread.
+ _ASSERTE(pThread == GetThread());
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ if (pThread == NULL)
+ COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
+
+ pThread->ClearAbortReason();
+
+ HELPER_METHOD_FRAME_END();
+
+}
+FCIMPLEND
+
+