// IID ICLRRuntimeHost4: uuid(64F6D366-D7C2-4F1F-B4B2-E8160CAC43AF)
cpp_quote("EXTERN_GUID(IID_ICLRRuntimeHost4, 0x64F6D366, 0xD7C2, 0x4F1F, 0xB4, 0xB2, 0xE8, 0x16, 0x0C, 0xAC, 0x43, 0xAF);")
-// IID IID_ICLRExecutionManager: uuid(1000A3E7-B420-4620-AE30-FB19B587AD1D)
-cpp_quote("EXTERN_GUID(IID_ICLRExecutionManager, 0x1000A3E7, 0xB420, 0x4620, 0xAE, 0x30, 0xFB, 0x19, 0xB5, 0x87, 0xAD, 0x1D);")
-
// IID ITypeName : uuid{B81FF171-20F3-11d2-8DCC-00A0C9B00522}
cpp_quote("EXTERN_GUID(IID_ITypeName, 0xB81FF171, 0x20F3, 0x11d2, 0x8d, 0xcc, 0x00, 0xa0, 0xc9, 0xb0, 0x05, 0x22);")
[out] int *pLatchedExitCode);
};
-[
- uuid(1000A3E7-B420-4620-AE30-FB19B587AD1D),
- version(1.0),
- helpstring("Pause and Resume Interface"),
- pointer_default(unique),
- local
-]
-interface ICLRExecutionManager : IUnknown
-{
- // Pause all managed threads
- // Parameters are ignored and reserved for future use.
- HRESULT Pause([in] DWORD dwAppDomainId, [in] DWORD dwFlags);
-
- // Resume managed threads
- // Parameters are ignored and reserved for future use.
- HRESULT Resume([in] DWORD dwAppDomainId);
-}
-
//*****************************************************************************
// Interface to utilize HostProtection
//*****************************************************************************
class Assembly;
-class CorExecutionManager
- : public ICLRExecutionManager
-{
-public:
- CorExecutionManager();
-
- STDMETHODIMP STDMETHODCALLTYPE Pause(DWORD dwAppDomainId, DWORD dwFlags);
- STDMETHODIMP STDMETHODCALLTYPE Resume(DWORD dwAppDomainId);
-
-private:
- DWORD m_dwFlags; //flags passed to the last Pause call.
- INT64 m_pauseStartTime;
-};
-
class CorRuntimeHostBase
{
protected:
, public IPrivateManagedExceptionReporting /* This interface is for internal Watson testing only*/
#endif // FEATURE_PAL
, public ICLRRuntimeHost4
- , public CorExecutionManager
{
friend struct _DacGlobals;
#endif /* __ICLRRuntimeHost4_FWD_DEFINED__ */
-#ifndef __ICLRExecutionManager_FWD_DEFINED__
-#define __ICLRExecutionManager_FWD_DEFINED__
-typedef interface ICLRExecutionManager ICLRExecutionManager;
-
-#endif /* __ICLRExecutionManager_FWD_DEFINED__ */
-
#ifndef __IHostNetCFDebugControlManager_FWD_DEFINED__
#define __IHostNetCFDebugControlManager_FWD_DEFINED__
EXTERN_GUID(IID_ICLRRuntimeHost, 0x90F1A06C, 0x7712, 0x4762, 0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02);
EXTERN_GUID(IID_ICLRRuntimeHost2, 0x712AB73F, 0x2C22, 0x4807, 0xAD, 0x7E, 0xF5, 0x01, 0xD7, 0xb7, 0x2C, 0x2D);
EXTERN_GUID(IID_ICLRRuntimeHost4, 0x64F6D366, 0xD7C2, 0x4F1F, 0xB4, 0xB2, 0xE8, 0x16, 0x0C, 0xAC, 0x43, 0xAF);
-EXTERN_GUID(IID_ICLRExecutionManager, 0x1000A3E7, 0xB420, 0x4620, 0xAE, 0x30, 0xFB, 0x19, 0xB5, 0x87, 0xAD, 0x1D);
EXTERN_GUID(IID_ITypeName, 0xB81FF171, 0x20F3, 0x11d2, 0x8d, 0xcc, 0x00, 0xa0, 0xc9, 0xb0, 0x05, 0x22);
EXTERN_GUID(IID_ITypeNameBuilder, 0xB81FF171, 0x20F3, 0x11d2, 0x8d, 0xcc, 0x00, 0xa0, 0xc9, 0xb0, 0x05, 0x23);
EXTERN_GUID(IID_ITypeNameFactory, 0xB81FF171, 0x20F3, 0x11d2, 0x8d, 0xcc, 0x00, 0xa0, 0xc9, 0xb0, 0x05, 0x21);
#endif /* __ICLRRuntimeHost2_INTERFACE_DEFINED__ */
-#ifndef __ICLRExecutionManager_INTERFACE_DEFINED__
-#define __ICLRExecutionManager_INTERFACE_DEFINED__
-
-/* interface ICLRExecutionManager */
-/* [object][local][unique][helpstring][version][uuid] */
-
-
-EXTERN_C const IID IID_ICLRExecutionManager;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("1000A3E7-B420-4620-AE30-FB19B587AD1D")
- ICLRExecutionManager : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Pause(
- /* [in] */ DWORD dwAppDomainId,
- /* [in] */ DWORD dwFlags) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Resume(
- /* [in] */ DWORD dwAppDomainId) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICLRExecutionManagerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICLRExecutionManager * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICLRExecutionManager * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICLRExecutionManager * This);
-
- HRESULT ( STDMETHODCALLTYPE *Pause )(
- ICLRExecutionManager * This,
- /* [in] */ DWORD dwAppDomainId,
- /* [in] */ DWORD dwFlags);
-
- HRESULT ( STDMETHODCALLTYPE *Resume )(
- ICLRExecutionManager * This,
- /* [in] */ DWORD dwAppDomainId);
-
- END_INTERFACE
- } ICLRExecutionManagerVtbl;
-
- interface ICLRExecutionManager
- {
- CONST_VTBL struct ICLRExecutionManagerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICLRExecutionManager_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICLRExecutionManager_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICLRExecutionManager_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICLRExecutionManager_Pause(This,dwAppDomainId,dwFlags) \
- ( (This)->lpVtbl -> Pause(This,dwAppDomainId,dwFlags) )
-
-#define ICLRExecutionManager_Resume(This,dwAppDomainId) \
- ( (This)->lpVtbl -> Resume(This,dwAppDomainId) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICLRExecutionManager_INTERFACE_DEFINED__ */
-
-
#ifndef __IHostNetCFDebugControlManager_INTERFACE_DEFINED__
#define __IHostNetCFDebugControlManager_INTERFACE_DEFINED__
class TypeEquivalenceHashTable;
class StringArrayList;
-extern INT64 g_PauseTime; // Total time in millisecond the CLR has been paused
-
#ifdef FEATURE_COMINTEROP
class ComCallWrapperCache;
struct SimpleComCallWrapper;
#undef FPO_ON
#endif
-extern INT64 g_PauseTime; // Total duration of all pauses in the runtime
-extern Volatile<BOOL> g_IsPaused; // True if the runtime is Paused for FAS
-extern CLREventStatic g_ClrResumeEvent; // Event fired when the runtime is resumed after a Pause for FAS
-INT64 AdditionalWait(INT64 sPauseTime, INT64 sTime, INT64 expDuration);
-
#endif // !_common_h_
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;
- }
+ GetThread()->UserSleep(iTime);
HELPER_METHOD_FRAME_END();
}
#include "excep.h"
#include "comwaithandle.h"
-
-//-----------------------------------------------------------------------------
-// ObjArrayHolder : ideal for holding a managed array of items. Will run
-// the ACQUIRE method sequentially on each item. Assume the ACQUIRE method
-// may possibly fail. If it does, only release the ones we've acquired.
-// Note: If a GC occurs during the ACQUIRE or RELEASE methods, you'll have to
-// explicitly gc protect the objectref.
-//-----------------------------------------------------------------------------
-template <typename TYPE, void (*ACQUIRE)(TYPE), void (*RELEASEF)(TYPE)>
-class ObjArrayHolder
-{
-
-public:
- ObjArrayHolder() {
- LIMITED_METHOD_CONTRACT;
- m_numAcquired = 0;
- m_pValues = NULL;
- }
-
- // Assuming ACQUIRE can throw an exception, we must put this logic
- // somewhere outside of the constructor. In C++, the destructor won't be
- // run if the constructor didn't complete.
- void Initialize(const unsigned int numElements, PTRARRAYREF* pValues) {
- WRAPPER_NO_CONTRACT;
- _ASSERTE(m_numAcquired == 0);
- m_numElements = numElements;
- m_pValues = pValues;
- for (unsigned int i=0; i<m_numElements; i++) {
- TYPE value = (TYPE) (*m_pValues)->GetAt(i);
- ACQUIRE(value);
- m_numAcquired++;
- }
- }
-
- ~ObjArrayHolder() {
- WRAPPER_NO_CONTRACT;
-
- GCX_COOP();
- for (unsigned int i=0; i<m_numAcquired; i++) {
- TYPE value = (TYPE) (*m_pValues)->GetAt(i);
- RELEASEF(value);
- }
- }
-
-private:
- unsigned int m_numElements;
- unsigned int m_numAcquired;
- PTRARRAYREF* m_pValues;
-
- FORCEINLINE ObjArrayHolder<TYPE, ACQUIRE, RELEASEF> &operator=(const ObjArrayHolder<TYPE, ACQUIRE, RELEASEF> &holder)
- {
- _ASSERTE(!"No assignment allowed");
- return NULL;
- }
-
- FORCEINLINE ObjArrayHolder(const ObjArrayHolder<TYPE, ACQUIRE, RELEASEF> &holder)
- {
- _ASSERTE(!"No copy construction allowed");
- }
-};
-
-INT64 AdditionalWait(INT64 sPauseTime, INT64 sTime, INT64 expDuration)
-{
- LIMITED_METHOD_CONTRACT;
-
- _ASSERTE(g_PauseTime >= sPauseTime);
-
- INT64 pauseTime = g_PauseTime - sPauseTime;
- // No pause was used inbetween this handle
- if(pauseTime <= 0)
- return 0;
-
- INT64 actDuration = CLRGetTickCount64() - sTime;
-
- // In case the CLR is paused inbetween a wait, this method calculates how much
- // the wait has to be adjusted to account for the CLR Freeze. Essentially all
- // pause duration has to be considered as "time that never existed".
- //
- // Two cases exists, consider that 10 sec wait is issued
- // Case 1: All pauses happened before the wait completes. Hence just the
- // pause time needs to be added back at the end of wait
- // 0 3 8 10
- // |-----------|###################|------>
- // 5-sec pause
- // ....................>
- // Additional 5 sec wait
- // |=========================>
- //
- // Case 2: Pauses ended after the wait completes.
- // 3 second of wait was left as the pause started at 7 so need to add that back
- // 0 7 10
- // |---------------------------|###########>
- // 5-sec pause 12
- // ...................>
- // Additional 3 sec wait
- // |==================>
- //
- // Both cases can be expressed in the same calculation
- // pauseTime: sum of all pauses that were triggered after the timer was started
- // expDuration: expected duration of the wait (without any pauses) 10 in the example
- // actDuration: time when the wait finished. Since the CLR is frozen during pause it's
- // max of timeout or pause-end. In case-1 it's 10, in case-2 it's 12
- INT64 additional = expDuration - (actDuration - pauseTime);
- if(additional < 0)
- additional = 0;
-
- return additional;
-}
-
FCIMPL2(INT32, WaitHandleNative::CorWaitOneNative, HANDLE handle, INT32 timeout)
{
FCALL_CONTRACT;
Thread* pThread = GET_THREAD();
- DWORD res = (DWORD) -1;
-
- // Support for pause/resume (FXFREEZE)
- while(true)
- {
- INT64 sPauseTime = g_PauseTime;
- INT64 sTime = CLRGetTickCount64();
- res = pThread->DoAppropriateWait(1, &handle, TRUE, timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx));
- if(res != WAIT_TIMEOUT)
- break;
- timeout = (INT32)AdditionalWait(sPauseTime, sTime, timeout);
- if(timeout == 0)
- break;
- }
-
- retVal = res;
+ retVal = pThread->DoAppropriateWait(1, &handle, TRUE, timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx));
HELPER_METHOD_FRAME_END();
return retVal;
}
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
- DWORD res = (DWORD) -1;
- {
- // Support for pause/resume (FXFREEZE)
- while(true)
- {
- INT64 sPauseTime = g_PauseTime;
- INT64 sTime = CLRGetTickCount64();
- res = pThread->DoAppropriateWait(numHandles, handleArray, waitForAll, timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx));
- if(res != WAIT_TIMEOUT)
- break;
- timeout = (INT32)AdditionalWait(sPauseTime, sTime, timeout);
- if(timeout == 0)
- break;
- }
- }
+ ret = pThread->DoAppropriateWait(numHandles, handleArray, waitForAll, timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx));
- ret = res;
-
HELPER_METHOD_FRAME_END();
return ret;
}
extern void PrintToStdOutW(const WCHAR *pwzString);
extern BOOL g_fEEHostedStartup;
-INT64 g_PauseTime; // Total time in millisecond the CLR has been paused
-Volatile<BOOL> g_IsPaused; // True if the runtime is paused (FAS)
-CLREventStatic g_ClrResumeEvent; // Event that is fired at FAS Resuming
-
-extern BYTE g_rbTestKeyBuffer[];
-
//***************************************************************************
ULONG CorRuntimeHostBase::m_Version = 0;
return S_OK;
}
-
-
-HRESULT SuspendEEForPause()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_PREEMPTIVE;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- HRESULT hr = S_OK;
-
- // In CoreCLR, we always resume from the same thread that paused. So we can simply suspend the EE from this thread,
- // knowing we'll restart from the same thread.
- ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER);
-
- return hr;
-}
-
-HRESULT RestartEEFromPauseAndSetResumeEvent()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_PREEMPTIVE;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- // see comments in SuspendEEFromPause
- ThreadSuspend::RestartEE(FALSE, TRUE);
-
- _ASSERTE(g_ClrResumeEvent.IsValid());
- g_ClrResumeEvent.Set();
-
- return S_OK;
-}
-
-
-
-CorExecutionManager::CorExecutionManager()
- : m_dwFlags(0), m_pauseStartTime(0)
-{
- LIMITED_METHOD_CONTRACT;
- g_IsPaused = FALSE;
- g_PauseTime = 0;
-}
-
-HRESULT CorExecutionManager::Pause(DWORD dwAppDomainId, DWORD dwFlags)
-{
- CONTRACTL
- {
- NOTHROW;
- if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- ENTRY_POINT; // This is called by a host.
- }
- CONTRACTL_END;
-
- HRESULT hr = S_OK;
-
-
- if(g_IsPaused)
- return E_FAIL;
-
- EX_TRY
- {
- if(!g_ClrResumeEvent.IsValid())
- g_ClrResumeEvent.CreateManualEvent(FALSE);
- else
- g_ClrResumeEvent.Reset();
-
- }
- EX_CATCH_HRESULT(hr);
-
- if (FAILED(hr))
- return hr;
-
- BEGIN_ENTRYPOINT_NOTHROW;
-
- m_dwFlags = dwFlags;
-
-
- if (SUCCEEDED(hr))
- {
- g_IsPaused = TRUE;
-
- hr = SuspendEEForPause();
-
- // Even though this is named with TickCount, it returns milliseconds
- m_pauseStartTime = (INT64)CLRGetTickCount64();
- }
-
- END_ENTRYPOINT_NOTHROW;
-
- return hr;
-}
-
-
-HRESULT CorExecutionManager::Resume(DWORD dwAppDomainId)
-{
- CONTRACTL
- {
- NOTHROW;
- if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- ENTRY_POINT; // This is called by a host.
- }
- CONTRACTL_END;
-
- HRESULT hr = S_OK;
-
-
- if(!g_IsPaused)
- return E_FAIL;
-
- // GCThread is the thread that did the Pause. Resume should also happen on that same thread
- Thread *pThread = GetThread();
- if(pThread != ThreadSuspend::GetSuspensionThread())
- {
- _ASSERTE(!"HOST BUG: The same thread that did Pause should do the Resume");
- return E_FAIL;
- }
-
- BEGIN_ENTRYPOINT_NOTHROW;
-
- // Even though this is named with TickCount, it returns milliseconds
- INT64 currTime = (INT64)CLRGetTickCount64();
- _ASSERTE(currTime >= m_pauseStartTime);
- _ASSERTE(m_pauseStartTime != 0);
-
- g_PauseTime += (currTime - m_pauseStartTime);
- g_IsPaused = FALSE;
-
- hr = RestartEEFromPauseAndSetResumeEvent();
-
-
- END_ENTRYPOINT_NOTHROW;
-
- return hr;
-}
-
-
#endif //!DACCESS_COMPILE
#ifndef DACCESS_COMPILE
*ppUnk = static_cast<ICLRRuntimeHost4 *>(this);
}
- else if (riid == IID_ICLRExecutionManager)
- {
- ULONG version = 2;
- if (m_Version == 0)
- FastInterlockCompareExchange((LONG*)&m_Version, version, 0);
-
- *ppUnk = static_cast<ICLRExecutionManager *>(this);
- }
#ifndef FEATURE_PAL
else if (riid == IID_IPrivateManagedExceptionReporting)
{
#endif // PROFILING_SUPPORTED
//************************************************************************************
-// To support fast application switch (FAS), one requirement is that the CPU
-// consumption during the time the CLR is paused should be 0. Given that the process
-// will be anyway suspended this should've been an NOP for CLR. However, in Mango
-// we ensured that no handle timed out or no other such context switch happens
-// during the pause time. To match that and also to ensure that in-between the
-// pause and when the process is suspended (~60 sec) no context switch happens due to
-// CLR handles (like Waits/sleeps due to calls from BCL) we call APC on these
-// Threads and make them wait on the resume handle
-void __stdcall PauseAPC(__in ULONG_PTR dwParam)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(g_IsPaused && (GetThread()->m_State & Thread::TS_Interruptible))
- {
- _ASSERTE(g_ClrResumeEvent.IsValid());
- EX_TRY {
- g_ClrResumeEvent.Wait(INFINITE, FALSE);
- }
- EX_CATCH {
- // Assert on debug builds
- _ASSERTE(FALSE);
- }
- EX_END_CATCH(SwallowAllExceptions);
- }
-}
-
-
-//************************************************************************************
//
// SuspendRuntime is responsible for ensuring that all managed threads reach a
// "safe point." It returns when all threads are known to be in "preemptive" mode.
#endif // DISABLE_THREADSUSPEND
}
- else
- {
- // To ensure 0 CPU utilization for FAS (see implementation of PauseAPC)
- // we queue the APC to all interruptable threads.
- if(g_IsPaused && (thread->m_State & Thread::TS_Interruptible))
- {
- HANDLE handle = thread->GetThreadHandle();
- QueueUserAPC((PAPCFUNC)PauseAPC, handle, APC_Code);
- }
- }
}
#ifdef _DEBUG
STRESS_LOG1(LF_SYNC, LL_INFO1000, " Thread %x went preemptive it is at a GC safe point\n", thread);
countThreads--;
thread->ResetThreadState(Thread::TS_GCSuspendPending);
-
- // To ensure 0 CPU utilization for FAS (see implementation of PauseAPC)
- // we queue the APC to all interruptable threads.
- if(g_IsPaused && (thread->m_State & Thread::TS_Interruptible))
- {
- HANDLE handle = thread->GetThreadHandle();
- QueueUserAPC((PAPCFUNC)PauseAPC, handle, APC_Code);
- }
}
}
continue;
#endif // DEBUGGING_SUPPORTED
- if(g_IsPaused)
- {
- _ASSERTE(g_ClrResumeEvent.IsValid());
- EX_TRY {
- g_ClrResumeEvent.Wait(INFINITE, TRUE);
- }
- EX_CATCH {
- // Assert on debug builds
- _ASSERTE(FALSE);
- }
- EX_END_CATCH(SwallowAllExceptions);
- }
-
if (!GCHeapUtilities::IsGCInProgress(FALSE) )
{
if (IgnoreNextSample)