case DLL_THREAD_DETACH:
{
#ifdef STRESS_LOG
- StressLog::ThreadDetach((ThreadStressLog*) ClrFlsGetValue(TlsIdx_StressLog));
+ StressLog::ThreadDetach();
#endif
#ifdef RSCONTRACTS
fRemap = true;
}EX_CATCH {}
- EX_END_CATCH(RethrowTerminalExceptions);
+ EX_END_CATCH(SwallowAllExceptions)
if (!fRemap)
{
m_runtimeOffsets.m_EEMaxFrameValue));
LOG((LF_CORDB, LL_INFO10000, " m_EEThreadDebuggerFilterContextOffset= 0x%08x\n",
m_runtimeOffsets.m_EEThreadDebuggerFilterContextOffset));
- LOG((LF_CORDB, LL_INFO10000, " m_EEThreadCantStopOffset= 0x%08x\n",
- m_runtimeOffsets.m_EEThreadCantStopOffset));
LOG((LF_CORDB, LL_INFO10000, " m_EEFrameNextOffset= 0x%08x\n",
m_runtimeOffsets.m_EEFrameNextOffset));
LOG((LF_CORDB, LL_INFO10000, " m_EEIsManagedExceptionStateMask= 0x%08x\n",
#endif
HRESULT EnableSSAfterBP();
- bool GetEEThreadCantStopHelper();
HRESULT GetTlsSlot(DWORD slot, REMOTE_PTR *pValue);
HRESULT SetTlsSlot(DWORD slot, REMOTE_PTR value);
- REMOTE_PTR GetPreDefTlsSlot(SIZE_T slot, bool * pRead);
+ REMOTE_PTR GetPreDefTlsSlot(SIZE_T slot);
void * m_pPatchSkipAddress;
UINT m_continueCountCached;
DWORD_PTR GetEEThreadValue();
- REMOTE_PTR GetEETlsDataBlock();
HRESULT GetClrModuleTlsDataAddress(REMOTE_PTR* pAddress);
public:
// return value of data in the slot, *pRead = true
// 2) On failure to read block (block doens't exist yet, any other failure)
// return value == 0 (assumed default, *pRead = false
-REMOTE_PTR CordbUnmanagedThread::GetPreDefTlsSlot(SIZE_T slot, bool * pRead)
+REMOTE_PTR CordbUnmanagedThread::GetPreDefTlsSlot(SIZE_T offset)
{
- REMOTE_PTR pBlock = (REMOTE_PTR) GetEETlsDataBlock();
-
- REMOTE_PTR data = 0;
-
- // We don't have a maximum size, but we know it's less than ~200. This assert
- // will catch if we're just passsing Garbage.
- _ASSERTE(slot < 200);
-
- bool dummy;
- if (pRead == NULL)
+ REMOTE_PTR tlsDataAddress;
+ HRESULT hr = GetClrModuleTlsDataAddress(&tlsDataAddress);
+ if (FAILED(hr))
{
- pRead = &dummy;
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: GetClrModuleTlsDataAddress FAILED %x for 0x%x\n", hr, m_id));
+ return NULL;
}
- if (pBlock != NULL)
- {
- REMOTE_PTR p = ((BYTE*) pBlock) + slot * sizeof(data);
-
- // Now read the "special" status out of the PreDef block.
- HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(p), &data);
-
- // The predef block should be valid at this point, so the ReadProcessMemory ought to work.
- SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
+ REMOTE_PTR data = 0;
- if (SUCCEEDED(hr))
- {
- *pRead = true;
- return data;
- }
+ // Read the thread's TLS value.
+ REMOTE_PTR slotAddr = (BYTE*)tlsDataAddress + offset;
+ hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(slotAddr), &data);
+ if (FAILED(hr))
+ {
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: failed to get TLS value: tlsData=0x%p offset=%d, err=%x\n",
+ tlsDataAddress, offset, hr));
+ return NULL;
}
- *pRead = false;
- return 0;
+ LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETV: EE Thread TLS value is 0x%x for 0x%x\n", data, m_id));
+ return data;
}
// Read the contents from a LS threads's TLS slot.
hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadTLS), pValue);
if (FAILED(hr))
{
- LOG((LF_CORDB, LL_INFO1000, "CUT::GTS: failed to read TLS value: computed addr=0x%p index=%d, err=%x\n",
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GTS: failed to read TLS value: computed addr=0x%p slot=%d, err=%x\n",
pEEThreadTLS, slot, hr));
return hr;
}
}
// Read the thread's TLS value.
- REMOTE_PTR EEThreadAddr = (BYTE*)tlsDataAddress + OFFSETOF__TLS__tls_CurrentThread;
+ REMOTE_PTR EEThreadAddr = (BYTE*)tlsDataAddress + GetProcess()->m_runtimeOffsets.m_TLSEEThreadOffset + OFFSETOF__TLS__tls_CurrentThread;
hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(EEThreadAddr), &ret);
if (FAILED(hr))
{
return E_FAIL;
}
- DWORD slot = (DWORD)(GetProcess()->m_runtimeOffsets.m_TLSIndex);
-
REMOTE_PTR clrModuleTlsDataAddr;
- hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)tlsArrayAddr + (slot & 0xFFFF) * sizeof(void*)), &clrModuleTlsDataAddr);
+ hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)tlsArrayAddr + GetProcess()->m_runtimeOffsets.m_TLSIndex * sizeof(void*)), &clrModuleTlsDataAddr);
if (FAILED(hr))
{
return hr;
return E_FAIL;
}
- *pAddress = (BYTE*) clrModuleTlsDataAddr + ((slot & 0x7FFF0000) >> 16);
+ *pAddress = (BYTE*) clrModuleTlsDataAddr;
return S_OK;
}
-// Gets the value of gCurrentThreadInfo.m_EETlsData
-REMOTE_PTR CordbUnmanagedThread::GetEETlsDataBlock()
-{
- REMOTE_PTR ret;
-
- REMOTE_PTR tlsDataAddress;
- HRESULT hr = GetClrModuleTlsDataAddress(&tlsDataAddress);
- if (FAILED(hr))
- {
- LOG((LF_CORDB, LL_INFO1000, "CUT::GEETDB: GetClrModuleTlsDataAddress FAILED %x for 0x%x\n", hr, m_id));
- return NULL;
- }
-
- REMOTE_PTR blockAddr = (BYTE*)tlsDataAddress + OFFSETOF__TLS__tls_EETlsData;
- hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(blockAddr), &ret);
- if (FAILED(hr))
- {
- LOG((LF_CORDB, LL_INFO1000, "CUT::GEETDB: failed to read EETlsData address: computed addr=0x%p offset=%d, err=%x\n",
- blockAddr, OFFSETOF__TLS__tls_EETlsData, hr));
- return NULL;
- }
-
- LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETDB: EETlsData address value is 0x%p for 0x%x\n", ret, m_id));
- return ret;
-}
-
/*
* GetEEDebuggerWord
*
return;
}
-// Currently, the EE manually tracks its "can't-stop" regions. This retrieves that manual tracking value.
-// @todo - This should eventually become deprecated since the Entire EE will be a can't-stop region.
-bool CordbUnmanagedThread::GetEEThreadCantStopHelper()
-{
- // Note: any failure to read memory is okay for this method. We simply say that the thread is not is a can't stop
- // state, and that's okay.
-
- REMOTE_PTR pEEThread;
-
- HRESULT hr = GetEEThreadPtr(&pEEThread);
-
- _ASSERTE(SUCCEEDED(hr));
- _ASSERTE(pEEThread != NULL);
-
- // Compute the address of the thread's debugger word #1
- DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets);
- void *pEEThreadCantStop = (BYTE*) pEEThread + pRO->m_EEThreadCantStopOffset;
-
- // Grab the debugger word #1 out of the EE Thread.
- DWORD EEThreadCantStop;
- hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadCantStop), &EEThreadCantStop);
-
- if (FAILED(hr))
- {
- LOG((LF_CORDB, LL_INFO1000, "CUT::GEETS: failed to read thread cant stop: 0x%08x + 0x%x = 0x%08x, err=%d\n",
- pEEThread, pRO->m_EEThreadCantStopOffset, pEEThreadCantStop, GetLastError()));
-
- return false;
- }
-
- LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETS: EE Thread cant stop is 0x%08x\n", EEThreadCantStop));
-
- // Looks like we've got it.
- if (EEThreadCantStop != 0)
- return true;
- else
- return false;
-}
-
-
// Is the thread in a "can't stop" region?
// "Can't-Stop" regions include anything that's "inside" the runtime; ie, the runtime has some
// synchronization mechanism that will halt this thread, and so we don't need to suspend it.
// them, they may be holding a lock that blocks the helper thread.
// The helper thread is marked as "special".
{
- SIZE_T idx = pRO->m_TLSIsSpecialIndex;
- REMOTE_PTR special = GetPreDefTlsSlot(idx, NULL);
+ REMOTE_PTR special = GetPreDefTlsSlot(pRO->m_TLSIsSpecialOffset);
// If it's a special thread
if ((special != 0) && (pEEThread == NULL))
// (or when we don't have a thread object).
// If a LS thread takes a debugger lock, it will increment the Can't-Stop count.
{
- SIZE_T idx = pRO->m_TLSCantStopIndex;
- REMOTE_PTR count = (REMOTE_PTR) GetPreDefTlsSlot(idx, NULL);
+ REMOTE_PTR count = GetPreDefTlsSlot(pRO->m_TLSCantStopOffset);
// Just a sanity check here. There's nothing special about 1000, but if the
// stop-count gets this big, 99% chance it's:
// This checks for an explicit "can't" stop region.
// Eventually, these explicit regions should become a complete subset of the other checks.
- if (GetEEThreadCantStopHelper())
+ REMOTE_PTR count = GetPreDefTlsSlot(GetProcess()->m_runtimeOffsets.m_TLSCantStopOffset);
+ if (count > 0)
return true;
-
// If we're in cooperative mode (either managed code or parts inside the runtime), then don't stop.
// Note we could remove this since the check is made in side of the DAC request below,
// but it's faster to look here.
// @dbgtodo inspection - this should all go away or be obtained from DacDbi Primitives.
g_pEEInterface->GetRuntimeOffsets(&pDebuggerRuntimeOffsets->m_TLSIndex,
- &pDebuggerRuntimeOffsets->m_TLSIsSpecialIndex,
- &pDebuggerRuntimeOffsets->m_TLSCantStopIndex,
+ &pDebuggerRuntimeOffsets->m_TLSEEThreadOffset,
+ &pDebuggerRuntimeOffsets->m_TLSIsSpecialOffset,
+ &pDebuggerRuntimeOffsets->m_TLSCantStopOffset,
&pDebuggerRuntimeOffsets->m_EEThreadStateOffset,
&pDebuggerRuntimeOffsets->m_EEThreadStateNCOffset,
&pDebuggerRuntimeOffsets->m_EEThreadPGCDisabledOffset,
&pDebuggerRuntimeOffsets->m_EEThreadSteppingStateMask,
&pDebuggerRuntimeOffsets->m_EEMaxFrameValue,
&pDebuggerRuntimeOffsets->m_EEThreadDebuggerFilterContextOffset,
- &pDebuggerRuntimeOffsets->m_EEThreadCantStopOffset,
&pDebuggerRuntimeOffsets->m_EEFrameNextOffset,
&pDebuggerRuntimeOffsets->m_EEIsManagedExceptionStateMask);
void *m_raiseExceptionAddr; // The address of kernel32!RaiseException in the debuggee
DWORD m_debuggerWordTLSIndex; // The TLS slot for the debugger word used in the debugger hijack functions
#endif // FEATURE_INTEROP_DEBUGGING
- SIZE_T m_TLSIndex; // The TLS index the CLR is using to hold Thread objects
- SIZE_T m_TLSIsSpecialIndex; // The index into the Predef block of the the "IsSpecial" status for a thread.
- SIZE_T m_TLSCantStopIndex; // The index into the Predef block of the the Can't-Stop count.
+ SIZE_T m_TLSIndex; // The TLS index of the thread-local storage for coreclr.dll
+ SIZE_T m_TLSEEThreadOffset; // TLS Offset of the Thread pointer.
+ SIZE_T m_TLSIsSpecialOffset; // TLS Offset of the "IsSpecial" status for a thread.
+ SIZE_T m_TLSCantStopOffset; // TLS Offset of the Can't-Stop count.
SIZE_T m_EEThreadStateOffset; // Offset of m_state in a Thread
SIZE_T m_EEThreadStateNCOffset; // Offset of m_stateNC in a Thread
SIZE_T m_EEThreadPGCDisabledOffset; // Offset of the bit for whether PGC is disabled or not in a Thread
DWORD m_EEThreadSteppingStateMask; // Mask for Thread::TSNC_DebuggerIsStepping
DWORD m_EEMaxFrameValue; // The max Frame value
SIZE_T m_EEThreadDebuggerFilterContextOffset; // Offset of debugger's filter context within a Thread Object.
- SIZE_T m_EEThreadCantStopOffset; // Offset of the can't stop count in a Thread
SIZE_T m_EEFrameNextOffset; // Offset of the next ptr in a Frame
DWORD m_EEIsManagedExceptionStateMask; // Mask for Thread::TSNC_DebuggerIsManagedException
void *m_pPatches; // Addr of patch table
return false;
}
-#define ClrFlsSetThreadType(type)
-
//
// Performance logging
//
// Keep leakage counters.
static size_t s_cLeakedBytes;
static size_t s_cNumFailures;
+
+ static thread_local LONG t_count;
#endif
static BOOL s_neverEnforceAsserts;
static void SetAssertEnforcement(BOOL value);
- static void ReleaseTls(void* pCountTLS);
-
private:
- static LONG* InitTls();
#ifdef _DEBUG
static LPCSTR AllocateDynamicMessage(const SString &s);
#endif
#include "debugmacros.h"
#include "clrtypes.h"
-inline LONG *CHECK::InitTls()
-{
-#pragma push_macro("HeapAlloc")
-#pragma push_macro("GetProcessHeap")
-#undef HeapAlloc
-#undef GetProcessHeap
-
- LONG *pCount = (LONG *)::HeapAlloc(GetProcessHeap(), 0, sizeof(LONG));
- if (pCount)
- *pCount = 0;
-
-#pragma pop_macro("HeapAlloc")
-#pragma pop_macro("GetProcessHeap")
- ClrFlsSetValue(TlsIdx_Check, pCount);
- ClrFlsAssociateCallback(TlsIdx_Check, ReleaseCheckTls);
- return pCount;
-}
-
-inline void CHECK::ReleaseTls(void* pCountTLS)
-{
-#pragma push_macro("HeapFree")
-#pragma push_macro("GetProcessHeap")
-#undef HeapFree
-#undef GetProcessHeap
- LONG* pCount = (LONG*) pCountTLS;
- if (pCount)
- ::HeapFree(GetProcessHeap(), 0, pCount);
-
-#pragma pop_macro("HeapFree")
-#pragma pop_macro("GetProcessHeap")
-}
-
FORCEINLINE BOOL CHECK::EnterAssert()
{
if (s_neverEnforceAsserts)
return FALSE;
#ifdef _DEBUG_IMPL
- m_pCount = (LONG *)ClrFlsGetValue(TlsIdx_Check);
- if (!m_pCount)
- {
- m_pCount = InitTls();
- if (!m_pCount)
- return FALSE;
- }
+ m_pCount = &t_count;
if (!*m_pCount)
{
{
#ifdef _DEBUG_IMPL
if (!m_pCount)
- m_pCount = (LONG *)ClrFlsGetValue(TlsIdx_Check);
+ m_pCount = &t_count;
- if (!m_pCount)
- return FALSE;
- else
- return *m_pCount;
+ return *m_pCount;
#else
return FALSE;
#endif
extern int RFS_HashStack();
#endif
-
-void ClrFlsAssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
-
-typedef LPVOID* (*CLRFLSGETBLOCK)();
-extern CLRFLSGETBLOCK __ClrFlsGetBlock;
-
-// Combining getter/setter into a single call
-inline void ClrFlsIncrementValue(DWORD slot, int increment)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
- _ASSERTE(increment != 0);
-
- void **block = (*__ClrFlsGetBlock)();
- size_t value;
-
- if (block != NULL)
- {
- value = (size_t) block[slot];
-
- _ASSERTE((increment > 0) || (value + increment < value));
- block[slot] = (void *) (value + increment);
- }
- else
- {
- BEGIN_PRESERVE_LAST_ERROR;
-
- IExecutionEngine * pEngine = GetExecutionEngine();
- value = (size_t) pEngine->TLS_GetValue(slot);
-
- _ASSERTE((increment > 0) || (value + increment < value));
- pEngine->TLS_SetValue(slot, (void *) (value + increment));
-
- END_PRESERVE_LAST_ERROR;
- }
-}
-
-
-inline void * ClrFlsGetValue (DWORD slot)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
- void **block = (*__ClrFlsGetBlock)();
- if (block != NULL)
- {
- return block[slot];
- }
- else
- {
- void * value = GetExecutionEngine()->TLS_GetValue(slot);
- return value;
- }
-}
-
-
-inline BOOL ClrFlsCheckValue(DWORD slot, void ** pValue)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
-#ifdef _DEBUG
- *pValue = ULongToPtr(0xcccccccc);
-#endif //_DEBUG
- void **block = (*__ClrFlsGetBlock)();
- if (block != NULL)
- {
- *pValue = block[slot];
- return TRUE;
- }
- else
- {
- BOOL result = GetExecutionEngine()->TLS_CheckValue(slot, pValue);
- return result;
- }
-}
-
-inline void ClrFlsSetValue(DWORD slot, void *pData)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
- void **block = (*__ClrFlsGetBlock)();
- if (block != NULL)
- {
- block[slot] = pData;
- }
- else
- {
- BEGIN_PRESERVE_LAST_ERROR;
-
- GetExecutionEngine()->TLS_SetValue(slot, pData);
-
- END_PRESERVE_LAST_ERROR;
- }
-}
-
#ifndef SELF_NO_HOST
LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes);
BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem);
HMODULE GetCLRModule ();
-extern void IncCantAllocCount();
-extern void DecCantAllocCount();
+extern thread_local int t_CantAllocCount;
+
+inline void IncCantAllocCount()
+{
+ t_CantAllocCount++;
+}
+
+inline void DecCantAllocCount()
+{
+ t_CantAllocCount--;
+}
class CantAllocHolder
{
};
// At places where want to allocate stress log, we need to first check if we are allowed to do so.
-// If ClrTlsInfo doesn't exist for this thread, we take it as can alloc
inline bool IsInCantAllocRegion ()
{
- size_t count = 0;
- if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
- {
- _ASSERTE (count >= 0);
- return count > 0;
- }
- return false;
+ return t_CantAllocCount != 0;
+}
+inline BOOL IsInCantAllocStressLogRegion()
+{
+ return t_CantAllocCount != 0;
}
-// for stress log the rule is more restrict, we have to check the global counter too
-extern BOOL IsInCantAllocStressLogRegion();
#endif
]
interface IExecutionEngine : IUnknown
{
- // Thread Local Storage is based on logical threads. The underlying
- // implementation could be threads, fibers, or something more exotic.
- // Slot numbers are predefined. This is not a general extensibility
- // mechanism.
-
- // Associate a callback function for releasing TLS on thread/fiber death.
- // This can be NULL.
- void TLS_AssociateCallback([in] DWORD slot, [in] PTLS_CALLBACK_FUNCTION callback);
-
- // Get the TLS block for fast Get/Set operations
- PVOID* TLS_GetDataBlock();
-
- // Get the value at a slot
- PVOID TLS_GetValue([in] DWORD slot);
-
- // Get the value at a slot, return FALSE if TLS info block doesn't exist
- BOOL TLS_CheckValue([in] DWORD slot, [out] PVOID * pValue);
-
- // Set the value at a slot
- void TLS_SetValue([in] DWORD slot, [in] PVOID pData);
-
- // Free TLS memory block and make callback
- void TLS_ThreadDetaching();
-
// Critical Sections are sometimes exposed to the host and therefore need to be
// reflected from all CLR DLLs to the EE.
//
#define CONTRACT_BITMASK_RESET(whichbit) (m_flags &= ~(whichbit))
#define CONTRACT_BITMASK_UPDATE(whichbit, value) ((value)?CONTRACT_BITMASK_SET(whichbit):CONTRACT_BITMASK_RESET(whichbit))
-
-// Stored in the FLS under TlsIdx_ClrDebugState.
struct ClrDebugState
{
private:
ClrDebugState *CLRInitDebugState();
ClrDebugState *GetClrDebugState(BOOL fAlloc = TRUE);
+extern thread_local ClrDebugState* t_pClrDebugState;
// This function returns a ClrDebugState if one has been created, but will not create one itself.
inline ClrDebugState *CheckClrDebugState()
{
STATIC_CONTRACT_LIMITED_METHOD;
- ClrDebugState *ret = (ClrDebugState*)ClrFlsGetValue(TlsIdx_ClrDebugState);
- return ret;
+ return t_pClrDebugState;
}
void CONTRACT_ASSERT(const char *szElaboration,
{
STATIC_CONTRACT_THROWS;
- // This had a _try/__except handler earlier that would swallow exceptions like
- // SO and breakpoint. Obviously, swallowing any of them is not the right thing to do.
- //
- // Thus, we replace it with EX_TRY/EX_CATCH that automatically kicks in with SO
- // handling if it sees any SO going past it. Also, C++ catch will not swallow
- // the breakpoint exception (which is what we want).
EX_TRY
{
PrettyPrintSignature(typePtr,
out->Shrink(0);
appendStr(out,"ERROR PARSING THE SIGNATURE");
}
-#if defined(__ILDASM__)
- // Dont allow ildasm to swallow bad SEH exceptions
- EX_END_CATCH(RethrowCorruptingExceptions);
-#else // __ILDASM__
EX_END_CATCH(SwallowAllExceptions);
-#endif // __ILDASM__
return(asString(out));
}
// See: https://github.com/dotnet/diagnostics/blob/master/src/inc/predeftlsslot.h
// ******************************************************************************
-// And here are the predefined slots for accessing TLS from various DLLs of the CLR.
-// Note that we want to support combinations of Debug and Retail DLLs for testing
-// purposes, so we burn the slots into the retail EE even if a debug CLR dll needs
-// them.
+// The historic location of ThreadType slot kept for compatibility with SOS
+// TODO: Introduce DAC API to make this hack unnecessary
enum PredefinedTlsSlots
{
- TlsIdx_OwnedCrstsChain, // slot to store the Crsts owned by this thread
- TlsIdx_Unused1,
- TlsIdx_Unused2,
- TlsIdx_Unused3,
- TlsIdx_AssertDlgStatus, // Whether the thread is displaying an assert dialog
- TlsIdx_StressLog,
- TlsIdx_CantStopCount, // Can't-stop counter for any thread
- TlsIdx_Check,
- TlsIdx_ForbidGCLoaderUseCount,
- TlsIdx_ClrDebugState, // Pointer to ClrDebugState* structure
- TlsIdx_Unused10,
-
- // Add more indices here.
- TlsIdx_ThreadType, // bit flags to indicate special thread's type
- TlsIdx_CantAllocCount, //Can't allocate memory on heap in this thread
-
- // A transient thread value that indicates this thread is currently walking its stack
- // or the stack of another thread. This value is useful to help short-circuit
- // some problematic checks in the loader, guarantee that types & assemblies
- // encountered during the walk must already be loaded, and provide information to control
- // assembly loading behavior during stack walks.
- //
- // This value is set around the main portions of the stack walk (as those portions may
- // enter the type & assembly loaders). This is also explicitly cleared while the
- // walking thread calls the stackwalker callback or needs to execute managed code, as
- // such calls may execute arbitrary code unrelated to the actual stack walking, and
- // may never return, in the case of exception stackwalk callbacks.
- TlsIdx_StackWalkerWalkingThread, // Thread* that the stack walker is currently walking.
-
- // Save the last exception info. Sometimes we need this info in our EX_CATCH, such as for SO.
- // It will be better if VC can supply this in catch(...) block.
- // !!! These data may become stale. Use it only inside exception handling code.
- // !!! Please access these fields through GetCurrentExceptionPointers which validates the data to some level.
- TlsIdx_EXCEPTION_CODE,
- TlsIdx_PEXCEPTION_RECORD,
- TlsIdx_PCONTEXT,
-
- MAX_PREDEFINED_TLS_SLOT
+ TlsIdx_ThreadType = 11 // bit flags to indicate special thread's type
};
enum TlsThreadTypeFlag // flag used for thread type in Tls data
ThreadType_GenericInstantiationCompare= 0x00020000, // Used to indicate that the thread is determining if a generic instantiation in an ngen image matches a lookup.
};
-static_assert(TlsIdx_ThreadType == 11, "SOS in diagnostics repo has a dependency on this value.");
-
#endif
static void Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread,
unsigned maxBytesTotal, HMODULE hMod);
static void Terminate(BOOL fProcessDetach=FALSE);
- static void ThreadDetach(ThreadStressLog *msgs); // call at DllMain THREAD_DETACH if you want to recycle thread logs
+ static void ThreadDetach(); // call at DllMain THREAD_DETACH if you want to recycle thread logs
static int NewChunk ()
{
return InterlockedIncrement (&theLog.totalChunk);
unsigned MaxSizeTotal; //maximum memory allowed for stress log
Volatile<LONG> totalChunk; //current number of total chunks allocated
Volatile<ThreadStressLog*> logs; // the list of logs for every thread.
- Volatile<unsigned> TLSslot; // Each thread gets a log this is used to fetch each threads log
+ unsigned padding; // Preserve the layout for SOS
Volatile<LONG> deadCount; // count of dead threads in the log
CRITSEC_COOKIE lock; // lock
unsigned __int64 tickFrequency; // number of ticks per second
FILETIME startTime; // time the application started
SIZE_T moduleOffset; // Used to compute format strings.
+ static thread_local ThreadStressLog* t_pCurrentThreadLog;
+
// private:
static void Enter(CRITSEC_COOKIE dummy = NULL);
static void Leave(CRITSEC_COOKIE dummy = NULL);
#if !defined(DACCESS_COMPILE)
+extern thread_local size_t t_ThreadType;
+
// check if current thread is a GC thread (concurrent or server)
inline BOOL IsGCSpecialThread ()
{
STATIC_CONTRACT_MODE_ANY;
STATIC_CONTRACT_CANNOT_TAKE_LOCK;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_GC);
+ return !!(t_ThreadType & ThreadType_GC);
}
// check if current thread is a Gate thread
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Gate);
+ return !!(t_ThreadType & ThreadType_Gate);
}
// check if current thread is a Timer thread
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Timer);
+ return !!(t_ThreadType & ThreadType_Timer);
}
// check if current thread is a debugger helper thread
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_DbgHelper);
+ return !!(t_ThreadType & ThreadType_DbgHelper);
}
// check if current thread is a debugger helper thread
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_ETWRundownThread);
+ return !!(t_ThreadType & ThreadType_ETWRundownThread);
}
// check if current thread is a generic instantiation lookup compare thread
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_GenericInstantiationCompare);
+ return !!(t_ThreadType & ThreadType_GenericInstantiationCompare);
}
// check if current thread is a thread which is performing shutdown
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Shutdown);
+ return !!(t_ThreadType & ThreadType_Shutdown);
}
inline BOOL IsThreadPoolIOCompletionSpecialThread ()
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Threadpool_IOCompletion);
+ return !!(t_ThreadType & ThreadType_Threadpool_IOCompletion);
}
inline BOOL IsThreadPoolWorkerSpecialThread ()
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Threadpool_Worker);
+ return !!(t_ThreadType & ThreadType_Threadpool_Worker);
}
inline BOOL IsWaitSpecialThread ()
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Wait);
+ return !!(t_ThreadType & ThreadType_Wait);
}
// check if current thread is a thread which is performing shutdown
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_DynamicSuspendEE);
+ return !!(t_ThreadType & ThreadType_DynamicSuspendEE);
}
inline BOOL IsFinalizerThread ()
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Finalizer);
+ return !!(t_ThreadType & ThreadType_Finalizer);
}
inline BOOL IsShutdownHelperThread ()
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_ShutdownHelper);
+ return !!(t_ThreadType & ThreadType_ShutdownHelper);
}
inline BOOL IsProfilerAttachThread ()
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_MODE_ANY;
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_ProfAPI_Attach);
-}
-
-// set specical type for current thread
-inline void ClrFlsSetThreadType (TlsThreadTypeFlag flag)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- ClrFlsSetValue (TlsIdx_ThreadType, (LPVOID)(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) |flag));
+ return !!(t_ThreadType & ThreadType_ProfAPI_Attach);
}
-// clear specical type for current thread
-inline void ClrFlsClearThreadType (TlsThreadTypeFlag flag)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- ClrFlsSetValue (TlsIdx_ThreadType, (LPVOID)(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ~flag));
-}
+// set special type for current thread
+void ClrFlsSetThreadType(TlsThreadTypeFlag flag);
+void ClrFlsClearThreadType(TlsThreadTypeFlag flag);
#endif //!DACCESS_COMPILE
-#ifdef DACCESS_COMPILE
-#define SET_THREAD_TYPE_STACKWALKER(pThread)
-#define CLEAR_THREAD_TYPE_STACKWALKER()
-#else // DACCESS_COMPILE
-#define SET_THREAD_TYPE_STACKWALKER(pThread) ClrFlsSetValue(TlsIdx_StackWalkerWalkingThread, pThread)
-#define CLEAR_THREAD_TYPE_STACKWALKER() ClrFlsSetValue(TlsIdx_StackWalkerWalkingThread, NULL)
-#endif // DACCESS_COMPILE
-
HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription);
-inline BOOL IsStackWalkerThread()
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
-#if defined(DACCESS_COMPILE)
- return FALSE;
-#else
- return ClrFlsGetValue (TlsIdx_StackWalkerWalkingThread) != NULL;
-#endif
-}
-
inline BOOL IsGCThread ()
{
STATIC_CONTRACT_NOTHROW;
#ifndef DACCESS_COMPILE
m_flag = flag;
- m_fPreviouslySet = (((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & flag);
+ m_fPreviouslySet = (t_ThreadType & flag);
// In debug builds, remember the full group of flags that were set at the time
// the constructor was called. This will be used in ASSERTs in the destructor
- INDEBUG(m_nPreviousFlagGroup = (size_t)ClrFlsGetValue (TlsIdx_ThreadType));
+ INDEBUG(m_nPreviousFlagGroup = t_ThreadType);
if (!m_fPreviouslySet)
{
// The expression below says that the only difference between the previous flag
// group and the current flag group should be m_flag (or no difference at all, if
// m_flag's state didn't actually change).
- _ASSERTE(((m_nPreviousFlagGroup ^ (size_t) ClrFlsGetValue(TlsIdx_ThreadType)) | (size_t) m_flag) == (size_t) m_flag);
+ _ASSERTE(((m_nPreviousFlagGroup ^ t_ThreadType) | (size_t) m_flag) == (size_t) m_flag);
if (m_fPreviouslySet)
{
INDEBUG(size_t m_nPreviousFlagGroup);
};
-class ClrFlsValueSwitch
-{
-public:
- ClrFlsValueSwitch (PredefinedTlsSlots slot, PVOID value)
- {
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
-#ifndef DACCESS_COMPILE
- m_slot = slot;
- m_PreviousValue = ClrFlsGetValue(slot);
- ClrFlsSetValue(slot, value);
-#endif // DACCESS_COMPILE
- }
-
- ~ClrFlsValueSwitch ()
- {
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
-#ifndef DACCESS_COMPILE
- ClrFlsSetValue(m_slot, m_PreviousValue);
-#endif // DACCESS_COMPILE
- }
-
-private:
- PVOID m_PreviousValue;
- PredefinedTlsSlots m_slot;
-};
-
-//*********************************************************************************
-
-// When we're hosted, operations called by the host (such as Thread::YieldTask)
-// may not cause calls back into the host, as the host needs not be reentrant.
-// Use the following holder for code in which calls into the host are forbidden.
-// (If a call into the host is attempted nevertheless, an assert will fire.)
-
-class ForbidCallsIntoHostOnThisThread
-{
-private:
- static Volatile<PVOID> s_pvOwningFiber;
-
- FORCEINLINE static BOOL Enter(BOOL)
- {
- WRAPPER_NO_CONTRACT;
- return InterlockedCompareExchangePointer(
- &s_pvOwningFiber, ClrTeb::GetFiberPtrId(), NULL) == NULL;
- }
-
- FORCEINLINE static void Leave(BOOL)
- {
- LIMITED_METHOD_CONTRACT;
- s_pvOwningFiber = NULL;
- }
-
-public:
- typedef ConditionalStateHolder<BOOL, ForbidCallsIntoHostOnThisThread::Enter, ForbidCallsIntoHostOnThisThread::Leave> Holder;
-
- FORCEINLINE static BOOL CanThisThreadCallIntoHost()
- {
- WRAPPER_NO_CONTRACT;
- return s_pvOwningFiber != ClrTeb::GetFiberPtrId();
- }
-};
-
-typedef ForbidCallsIntoHostOnThisThread::Holder ForbidCallsIntoHostOnThisThreadHolder;
-
-FORCEINLINE BOOL CanThisThreadCallIntoHost()
-{
- WRAPPER_NO_CONTRACT;
- return ForbidCallsIntoHostOnThisThread::CanThisThreadCallIntoHost();
-}
-
//*********************************************************************************
#include "contract.inl"
void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePath=NULL);
-
-// Helper to support termination due to heap corruption
-// It's not supported on Win2K, so we have to manually delay load it
-void EnableTerminationOnHeapCorruption();
-
-
-
namespace Clr { namespace Util
{
IExecutionEngine : public IUnknown
{
public:
- virtual void STDMETHODCALLTYPE TLS_AssociateCallback(
- /* [in] */ DWORD slot,
- /* [in] */ PTLS_CALLBACK_FUNCTION callback) = 0;
-
- virtual PVOID *STDMETHODCALLTYPE TLS_GetDataBlock( void) = 0;
-
- virtual PVOID STDMETHODCALLTYPE TLS_GetValue(
- /* [in] */ DWORD slot) = 0;
-
- virtual BOOL STDMETHODCALLTYPE TLS_CheckValue(
- /* [in] */ DWORD slot,
- /* [out] */ PVOID *pValue) = 0;
-
- virtual void STDMETHODCALLTYPE TLS_SetValue(
- /* [in] */ DWORD slot,
- /* [in] */ PVOID pData) = 0;
-
- virtual void STDMETHODCALLTYPE TLS_ThreadDetaching( void) = 0;
-
virtual CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(
/* [in] */ LPCSTR szTag,
/* [in] */ LPCSTR level,
ULONG ( STDMETHODCALLTYPE *Release )(
IExecutionEngine * This);
- void ( STDMETHODCALLTYPE *TLS_AssociateCallback )(
- IExecutionEngine * This,
- /* [in] */ DWORD slot,
- /* [in] */ PTLS_CALLBACK_FUNCTION callback);
-
- PVOID *( STDMETHODCALLTYPE *TLS_GetDataBlock )(
- IExecutionEngine * This);
-
- PVOID ( STDMETHODCALLTYPE *TLS_GetValue )(
- IExecutionEngine * This,
- /* [in] */ DWORD slot);
-
- BOOL ( STDMETHODCALLTYPE *TLS_CheckValue )(
- IExecutionEngine * This,
- /* [in] */ DWORD slot,
- /* [out] */ PVOID *pValue);
-
- void ( STDMETHODCALLTYPE *TLS_SetValue )(
- IExecutionEngine * This,
- /* [in] */ DWORD slot,
- /* [in] */ PVOID pData);
-
- void ( STDMETHODCALLTYPE *TLS_ThreadDetaching )(
- IExecutionEngine * This);
-
CRITSEC_COOKIE ( STDMETHODCALLTYPE *CreateLock )(
IExecutionEngine * This,
/* [in] */ LPCSTR szTag,
#define IExecutionEngine_Release(This) \
( (This)->lpVtbl -> Release(This) )
-
-#define IExecutionEngine_TLS_AssociateCallback(This,slot,callback) \
- ( (This)->lpVtbl -> TLS_AssociateCallback(This,slot,callback) )
-
-#define IExecutionEngine_TLS_GetDataBlock(This) \
- ( (This)->lpVtbl -> TLS_GetDataBlock(This) )
-
-#define IExecutionEngine_TLS_GetValue(This,slot) \
- ( (This)->lpVtbl -> TLS_GetValue(This,slot) )
-
-#define IExecutionEngine_TLS_CheckValue(This,slot,pValue) \
- ( (This)->lpVtbl -> TLS_CheckValue(This,slot,pValue) )
-
-#define IExecutionEngine_TLS_SetValue(This,slot,pData) \
- ( (This)->lpVtbl -> TLS_SetValue(This,slot,pData) )
-
-#define IExecutionEngine_TLS_ThreadDetaching(This) \
- ( (This)->lpVtbl -> TLS_ThreadDetaching(This) )
-
#define IExecutionEngine_CreateLock(This,szTag,level,flags) \
( (This)->lpVtbl -> CreateLock(This,szTag,level,flags) )
#ifdef _DEBUG
size_t CHECK::s_cLeakedBytes = 0;
size_t CHECK::s_cNumFailures = 0;
+
+thread_local LONG CHECK::t_count;
#endif
BOOL CHECK::s_neverEnforceAsserts = 0;
return p;
}
-void WINAPI ReleaseCheckTls(LPVOID pTlsData)
-{
- CHECK::ReleaseTls(pTlsData);
-}
-
#endif
CoreClrCallbacks g_CoreClrCallbacks;
-
-// In some cirumstance (e.g, the thread suspecd another thread), allocation on heap
-// could cause dead lock. We use a counter in TLS to indicate the current thread is not allowed
-// to do heap allocation.
-//In cases where CLRTlsInfo doesn't exist and we still want to track CantAlloc info (this is important to
-//stress log), We use a global counter. This introduces the problem where one thread could disable allocation
-//for another thread, but the cases should be rare (we limit the use to stress log for now) and the period
-//should (MUST) be short
-//only stress log check this counter
-
-struct CantAllocThread
-{
- PVOID m_fiberId;
- LONG m_CantCount;
-};
-
-#define MaxCantAllocThreadNum 100
-static CantAllocThread g_CantAllocThreads[MaxCantAllocThreadNum] = {};
-static Volatile<LONG> g_CantAllocStressLogCount = 0;
-
-void IncCantAllocCount()
-{
- size_t count = 0;
- if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
- {
- _ASSERTE (count >= 0);
- ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count+1));
- return;
- }
- PVOID fiberId = ClrTeb::GetFiberPtrId();
- for (int i = 0; i < MaxCantAllocThreadNum; i ++)
- {
- if (g_CantAllocThreads[i].m_fiberId == fiberId)
- {
- g_CantAllocThreads[i].m_CantCount ++;
- return;
- }
- }
- for (int i = 0; i < MaxCantAllocThreadNum; i ++)
- {
- if (g_CantAllocThreads[i].m_fiberId == NULL)
- {
- if (InterlockedCompareExchangeT(&g_CantAllocThreads[i].m_fiberId, fiberId, NULL) == NULL)
- {
- _ASSERTE(g_CantAllocThreads[i].m_CantCount == 0);
- g_CantAllocThreads[i].m_CantCount = 1;
- return;
- }
- }
- }
- count = InterlockedIncrement (&g_CantAllocStressLogCount);
- _ASSERTE (count >= 1);
- return;
-}
-
-void DecCantAllocCount()
-{
- size_t count = 0;
- if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
- {
- if (count > 0)
- {
- ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count-1));
- return;
- }
- }
- PVOID fiberId = ClrTeb::GetFiberPtrId();
- for (int i = 0; i < MaxCantAllocThreadNum; i ++)
- {
- if (g_CantAllocThreads[i].m_fiberId == fiberId)
- {
- _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0);
- g_CantAllocThreads[i].m_CantCount --;
- if (g_CantAllocThreads[i].m_CantCount == 0)
- {
- g_CantAllocThreads[i].m_fiberId = NULL;
- }
- return;
- }
- }
- _ASSERTE (g_CantAllocStressLogCount > 0);
- InterlockedDecrement (&g_CantAllocStressLogCount);
- return;
-
-}
-
-// for stress log the rule is more restrict, we have to check the global counter too
-BOOL IsInCantAllocStressLogRegion()
-{
- size_t count = 0;
- if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
- {
- if (count > 0)
- {
- return true;
- }
- }
- PVOID fiberId = ClrTeb::GetFiberPtrId();
- for (int i = 0; i < MaxCantAllocThreadNum; i ++)
- {
- if (g_CantAllocThreads[i].m_fiberId == fiberId)
- {
- _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0);
- return true;
- }
- }
-
- return g_CantAllocStressLogCount > 0;
-
-}
-
+thread_local int t_CantAllocCount;
#ifdef FAILPOINTS_ENABLED
typedef int (*FHashStack) ();
#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+thread_local ClrDebugState* t_pClrDebugState;
+
// Fls callback to deallocate ClrDebugState when our FLS block goes away.
void FreeClrDebugState(LPVOID pTlsData)
{
//=============================================================================================
ClrDebugState *CLRInitDebugState()
{
- // workaround!
- //
- // The existing Fls apis didn't provide the support we need and adding support cleanly is
- // messy because of the brittleness of IExecutionEngine.
- //
- // To understand this function, you need to know that the Fls routines have special semantics
- // for the TlsIdx_ClrDebugState slot:
- //
- // - FlsSetValue will never throw. If it fails due to OOM on creation of the slot storage,
- // it will silently bail. Thus, we must do a confirming FlsGetValue before we can conclude
- // that the SetValue succeeded.
- //
- // - FlsAssociateCallback will not complain about multiple sets of the callback.
- //
- // - The mscorwks implemention of FlsAssociateCallback will ignore the passed in value
- // and use the version of FreeClrDebugState compiled into mscorwks. This is needed to
- // avoid dangling pointer races on shutdown.
-
-
// This is our global "bad" debug state that thread use when they OOM on CLRInitDebugState.
// We really only need to initialize it once but initializing each time is convenient
// and has low perf impact.
ClrDebugState *pClrDebugState = NULL;
DbgStateLockData *pNewLockData = NULL;
- // We call this first partly to force a CheckThreadState. We've hopefully chased out all the
- // recursive contract calls inside here but if we haven't, it's best to get them out of the way
- // early.
- ClrFlsAssociateCallback(TlsIdx_ClrDebugState, FreeClrDebugState);
-
-
if (AreContractsShutoff())
{
pNewClrDebugState = NULL;
//
// So we must make one last check to see if the ClrDebugState still needs creating.
//
- ClrDebugState *pTmp = (ClrDebugState*)(ClrFlsGetValue(TlsIdx_ClrDebugState));
+ ClrDebugState *pTmp = t_pClrDebugState;
if (pTmp != NULL)
{
// Recursive call set up ClrDebugState for us
_ASSERTE(pClrDebugState != NULL);
-
- ClrFlsSetValue(TlsIdx_ClrDebugState, (LPVOID)pClrDebugState);
-
- // For the ClrDebugState index, ClrFlsSetValue does *not* throw on OOM.
- // Instead, it silently throws away the value. So we must now do a confirming
- // FlsGet to learn if our Set succeeded.
- if (ClrFlsGetValue(TlsIdx_ClrDebugState) == NULL)
- {
- // Our FlsSet didn't work. That means it couldn't allocate the master FLS block for our thread.
- // Now we're a bad state because not only can't we succeed, we can't record that we didn't succeed.
- // And it's invalid to return a BadClrDebugState here only to return a good debug state later.
- //
- // So we now take the drastic step of forcing all future ClrInitDebugState calls to return the OOM state.
- ShutoffContracts();
- pClrDebugState = &gBadClrDebugState;
-
- // Try once more time to set the FLS (if it doesn't work, the next call will keep cycling through here
- // until it does succeed.)
- ClrFlsSetValue(TlsIdx_ClrDebugState, &gBadClrDebugState);
- }
-
+ t_pClrDebugState = pClrDebugState;
#if defined(_DEBUG)
// The ClrDebugState we allocated above made it into FLS iff
}
}
-void ClrFlsAssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
-{
- WRAPPER_NO_CONTRACT;
-
- GetExecutionEngine()->TLS_AssociateCallback(slot, callback);
-}
-
-LPVOID *ClrFlsGetBlockGeneric()
-{
- WRAPPER_NO_CONTRACT;
-
- return (LPVOID *) GetExecutionEngine()->TLS_GetDataBlock();
-}
-
-CLRFLSGETBLOCK __ClrFlsGetBlock = ClrFlsGetBlockGeneric;
-
CRITSEC_COOKIE ClrCreateCriticalSection(CrstType crstType, CrstFlags flags)
{
WRAPPER_NO_CONTRACT;
RaiseFailFastException(NULL, NULL, 0);
}
-// Whether this thread is already displaying an assert dialog.
-BOOL IsDisplayingAssertDlg()
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_DEBUG_ONLY;
-
- size_t flag = 0;
- if (ClrFlsCheckValue(TlsIdx_AssertDlgStatus, (LPVOID *)&flag))
- {
- return (flag != 0);
- }
- return FALSE;
-}
-
-void SetDisplayingAssertDlg(BOOL value)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_DEBUG_ONLY;
-
- ClrFlsSetValue(TlsIdx_AssertDlgStatus, (LPVOID)(size_t)value);
-}
+thread_local bool f_bDisplayingAssertDlg;
VOID LogAssert(
LPCSTR szFile,
TerminateOnAssert();
}
- if (IsDisplayingAssertDlg())
+ if (f_bDisplayingAssertDlg)
{
// We are already displaying an assert dialog box on this thread. The reason why we came here is
// the message loop run by the API we call to display the UI. A message was dispatched and execution
return false;
}
- SetDisplayingAssertDlg(TRUE);
+ f_bDisplayingAssertDlg = true;
// Tell user there was an error.
_DbgBreakCount++;
}
--_DbgBreakCount;
- SetDisplayingAssertDlg(FALSE);
+ f_bDisplayingAssertDlg = false;
switch(ret)
{
GetHRMsg(hresult, result);
}
-#if !defined(DACCESS_COMPILE)
-
-void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo DEBUG_ARG(bool checkExceptionRecordLocation))
-{
- WRAPPER_NO_CONTRACT;
-
- PEXCEPTION_RECORD pRecord = (PEXCEPTION_RECORD)ClrFlsGetValue(TlsIdx_PEXCEPTION_RECORD);
- PCONTEXT pContext = (PCONTEXT)ClrFlsGetValue(TlsIdx_PCONTEXT);
-
- pExceptionInfo->ContextRecord = pContext;
- pExceptionInfo->ExceptionRecord = pRecord;
-
-#ifdef _DEBUG
- if (pRecord != NULL && checkExceptionRecordLocation)
- {
- _ASSERTE ((PVOID)(pRecord) > (PVOID)(&pRecord));
- }
-#endif
-}
-#endif // !defined(DACCESS_COMPILE)
-
-DWORD GetCurrentExceptionCode()
-{
- WRAPPER_NO_CONTRACT;
- SUPPORTS_DAC_HOST_ONLY;
-
- return (DWORD)(size_t)ClrFlsGetValue(TlsIdx_EXCEPTION_CODE);
-}
-
//===========================================================================================
// These abstractions hide the difference between legacy desktop CLR's (that don't support
// side-by-side-inproc and rely on a fixed SEH code to identify managed exceptions) and
#include "mscoree.h"
#include "clrinternal.h"
#include "hostimpl.h"
-#include "predeftlsslot.h"
// to avoid to include clrhost.h in this file
#ifdef FAILPOINTS_ENABLED
extern int RFS_HashStack();
#endif
-#ifndef __GNUC__
-static __declspec(thread) void** t_pBlock;
-#else // !__GNUC__
-static thread_local void** t_pBlock;
-#endif // !__GNUC__
-
-static PTLS_CALLBACK_FUNCTION Callbacks[MAX_PREDEFINED_TLS_SLOT];
-
#ifdef SELF_NO_HOST
HANDLE (*g_fnGetExecutableHeapHandle)();
#endif
-extern LPVOID* (*__ClrFlsGetBlock)();
-
-//
-// FLS getter to avoid unnecessary indirection via execution engine.
-//
-LPVOID* ClrFlsGetBlockDirect()
-{
- return t_pBlock;
-}
-
-//
-// utility functions for tls functionality
-//
-static void **CheckThreadState(DWORD slot, BOOL force = TRUE)
-{
- // Treat as a runtime assertion, since the invariant spans many DLLs.
- _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT);
-
- if (__ClrFlsGetBlock != ClrFlsGetBlockDirect)
- {
- // Switch to faster TLS getter now that the TLS slot is initialized
- __ClrFlsGetBlock = ClrFlsGetBlockDirect;
- }
-
- void **pTlsData = t_pBlock;
-
- if (pTlsData == 0 && force) {
-
- // !!! Contract uses our TLS support. Contract may be used before our host support is set up.
- // !!! To better support contract, we call into OS for memory allocation.
- pTlsData = (void**) ::HeapAlloc(GetProcessHeap(),0,MAX_PREDEFINED_TLS_SLOT*sizeof(void*));
-
-
- if (pTlsData == NULL)
- {
- // workaround! We don't want exceptions being thrown during ClrInitDebugState. Just return NULL out of TlsSetValue.
- // ClrInitDebugState will do a confirming FlsGet to see if the value stuck.
-
- // If this is for the stack probe, and we failed to allocate memory for it, we won't
- // put in a guard page.
- if (slot == TlsIdx_ClrDebugState)
- {
- return NULL;
- }
- RaiseException(STATUS_NO_MEMORY, 0, 0, NULL);
- }
- for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++)
- pTlsData[i] = 0;
- t_pBlock = pTlsData;
- }
-
- return pTlsData;
-} // CheckThreadState
+thread_local size_t t_ThreadType;
HRESULT STDMETHODCALLTYPE UtilExecutionEngine::QueryInterface(REFIID id, void **pInterface)
{
return 1;
}
-VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
-{
- CheckThreadState(slot);
-
- // They can toggle between a callback and no callback. But anything else looks like
- // confusion on their part.
- //
- // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But
- // all the callbacks are equally good.)
- _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0);
- Callbacks[slot] = callback;
-}
-
-LPVOID* STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetDataBlock()
-{
- return t_pBlock;
-}
-
-LPVOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetValue(DWORD slot)
-{
- void **pTlsData = CheckThreadState(slot, FALSE);
- if (pTlsData)
- return pTlsData[slot];
- else
- return NULL;
-}
-
-BOOL STDMETHODCALLTYPE UtilExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue)
-{
- void **pTlsData = CheckThreadState(slot, FALSE);
- if (pTlsData)
- {
- *pValue = pTlsData[slot];
- return TRUE;
- }
- return FALSE;
-}
-
-VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData)
-{
- void **pTlsData = CheckThreadState(slot);
- if (pTlsData) // Yes, CheckThreadState(slot, TRUE) can return NULL now.
- {
- pTlsData[slot] = pData;
- }
-}
-
-VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_ThreadDetaching()
-{
- void **pTlsData = CheckThreadState(0, FALSE);
- if (pTlsData)
- {
- for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++)
- {
- // If we have some data and a callback, issue it.
- if (Callbacks[i] != 0 && pTlsData[i] != 0)
- (*Callbacks[i])(pTlsData[i]);
- }
- ::HeapFree (GetProcessHeap(),0,pTlsData);
-
- }
-}
-
CRITSEC_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
{
CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
ULONG STDMETHODCALLTYPE Release();
//***************************************************************************
- // IExecutionEngine methods for TLS
- //***************************************************************************
-
- // Associate a callback for cleanup with a TLS slot
- VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
- // Get the master TLS slot index
- LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
-
- // Get the value at a slot
- LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
-
- // Get the value at a slot, return FALSE if TLS info block doesn't exist
- BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
- // Set the value at a slot
- VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
- // Free TLS memory block and make callback
- VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
-
- //***************************************************************************
// IExecutionEngine methods for locking
//***************************************************************************
{
STATIC_CONTRACT_WRAPPER;
-#ifdef SELF_NO_HOST
- if (TRUE)
-#else //!SELF_NO_HOST
- if (CanThisThreadCallIntoHost())
-#endif //!SELF_NO_HOST
- {
- va_list args;
- va_start(args, fmt);
- LogSpewValist (facility, level, fmt, args);
- va_end(args);
- }
- else
- {
- // Cannot acquire the required lock, as this would call back
- // into the host. Eat the log message.
- }
+ va_list args;
+ va_start(args, fmt);
+ LogSpewValist (facility, level, fmt, args);
+ va_end(args);
}
VOID LogSpew2(DWORD facility2, DWORD level, const char *fmt, ... )
{
STATIC_CONTRACT_WRAPPER;
-#ifdef SELF_NO_HOST
- if (TRUE)
-#else //!SELF_NO_HOST
- if (CanThisThreadCallIntoHost())
-#endif //!SELF_NO_HOST
- {
- va_list args;
- va_start(args, fmt);
- LogSpew2Valist(facility2, level, fmt, args);
- va_end(args);
- }
- else
- {
- // Cannot acquire the required lock, as this would call back
- // into the host. Eat the log message.
- }
+ va_list args;
+ va_start(args, fmt);
+ LogSpew2Valist(facility2, level, fmt, args);
+ va_end(args);
}
VOID LogSpewAlways (const char *fmt, ... )
{
STATIC_CONTRACT_WRAPPER;
-#ifdef SELF_NO_HOST
- if (TRUE)
-#else //!SELF_NO_HOST
- if (CanThisThreadCallIntoHost())
-#endif //!SELF_NO_HOST
- {
- va_list args;
- va_start(args, fmt);
- LogSpewValist (LF_ALWAYS, LL_ALWAYS, fmt, args);
- va_end(args);
- }
- else
- {
- // Cannot acquire the required lock, as this would call back
- // into the host. Eat the log message.
- }
+ va_list args;
+ va_start(args, fmt);
+ LogSpewValist (LF_ALWAYS, LL_ALWAYS, fmt, args);
+ va_end(args);
}
#endif // LOGGING
SidBuffer sidTargetProcessAppContainer;
// Get sid for current process.
- EX_TRY
+ if (SUCCEEDED(sidCurrentProcess.InitFromProcessNoThrow(GetCurrentProcessId())))
{
- sidCurrentProcess.InitFromProcess(GetCurrentProcessId()); // throw on error.
pCurrentProcessSid = sidCurrentProcess.GetSid().RawSid();
cSid++;
}
- EX_CATCH
- {
- }
- EX_END_CATCH(RethrowTerminalExceptions);
// Get sid for target process.
- EX_TRY
+ if (SUCCEEDED(sidTargetProcess.InitFromProcessNoThrow(pid)))
{
- sidTargetProcess.InitFromProcess(pid); // throws on error.
pTargetProcessSid = sidTargetProcess.GetSid().RawSid();
cSid++;
}
- EX_CATCH
- {
- }
- EX_END_CATCH(RethrowTerminalExceptions);
//FISHY: what is the scenario where only one of the above calls succeeds?
if (cSid == 0)
#if !defined(STRESS_LOG_READONLY)
HANDLE StressLogChunk::s_LogChunkHeap = NULL;
+thread_local ThreadStressLog* StressLog::t_pCurrentThreadLog;
#endif // !STRESS_LOG_READONLY
/*********************************************************************************/
return;
}
- _ASSERTE (theLog.TLSslot == (unsigned int)TLS_OUT_OF_INDEXES);
theLog.lock = ClrCreateCriticalSection(CrstStressLog,(CrstFlags)(CRST_UNSAFE_ANYMODE|CRST_DEBUGGER_THREAD));
// StressLog::Terminate is going to free memory.
if (maxBytesPerThread < STRESSLOG_CHUNK_SIZE)
theLog.facilitiesToLog = facilities | LF_ALWAYS;
theLog.levelToLog = level;
theLog.deadCount = 0;
- theLog.TLSslot = TlsIdx_StressLog;
theLog.tickFrequency = getTickFrequency();
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_FORBID_FAULT;
- if (theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES) {
- theLog.facilitiesToLog = 0;
+ theLog.facilitiesToLog = 0;
- StressLogLockHolder lockh(theLog.lock, FALSE);
- if (!fProcessDetach) {
- lockh.Acquire(); lockh.Release(); // The Enter() Leave() forces a memory barrier on weak memory model systems
- // we want all the other threads to notice that facilitiesToLog is now zero
-
- // This is not strictly threadsafe, since there is no way of insuring when all the
- // threads are out of logMsg. In practice, since they can no longer enter logMsg
- // and there are no blocking operations in logMsg, simply sleeping will insure
- // that everyone gets out.
- ClrSleepEx(2, FALSE);
- lockh.Acquire();
- }
+ StressLogLockHolder lockh(theLog.lock, FALSE);
+ if (!fProcessDetach) {
+ lockh.Acquire(); lockh.Release(); // The Enter() Leave() forces a memory barrier on weak memory model systems
+ // we want all the other threads to notice that facilitiesToLog is now zero
+
+ // This is not strictly threadsafe, since there is no way of insuring when all the
+ // threads are out of logMsg. In practice, since they can no longer enter logMsg
+ // and there are no blocking operations in logMsg, simply sleeping will insure
+ // that everyone gets out.
+ ClrSleepEx(2, FALSE);
+ lockh.Acquire();
+ }
- // Free the log memory
- ThreadStressLog* ptr = theLog.logs;
- theLog.logs = 0;
- while(ptr != 0) {
- ThreadStressLog* tmp = ptr;
- ptr = ptr->next;
- delete tmp;
- }
+ // Free the log memory
+ ThreadStressLog* ptr = theLog.logs;
+ theLog.logs = 0;
+ while(ptr != 0) {
+ ThreadStressLog* tmp = ptr;
+ ptr = ptr->next;
+ delete tmp;
+ }
- theLog.TLSslot = TLS_OUT_OF_INDEXES;
- if (!fProcessDetach) {
- lockh.Release();
- }
+ if (!fProcessDetach) {
+ lockh.Release();
}
+
#if !defined (STRESS_LOG_READONLY)
if (StressLogChunk::s_LogChunkHeap != NULL && StressLogChunk::s_LogChunkHeap != ClrGetProcessHeap ())
{
}
/*********************************************************************************/
-/* create a new thread stress log buffer associated with Thread local slot TLSslot, for the Stress log */
+/* create a new thread stress log buffer associated with Thread local slot, for the Stress log */
ThreadStressLog* StressLog::CreateThreadStressLog() {
CONTRACTL
static PVOID callerID = NULL;
- ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot);
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
if (msgs != NULL)
{
return msgs;
}
//if we are not allowed to allocate stress log, we should not even try to take the lock
- if (!StressLogChunk::s_LogChunkHeap || !CanThisThreadCallIntoHost() || IsInCantAllocStressLogRegion ())
+ if (!StressLogChunk::s_LogChunkHeap || IsInCantAllocStressLogRegion ())
{
return NULL;
}
// ClrFlsSetValue can throw an OOM exception the first time its called on a thread for a given slot. We go
// ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if
// we fail.
- ClrFlsSetValue(theLog.TLSslot, NULL);
+ t_pCurrentThreadLog = NULL;
}
#pragma warning(suppress: 4101)
PAL_CPP_CATCH_DERIVED(OutOfMemoryException, obj)
}
CONTRACTL_END;
- _ASSERTE(theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES); // because facilitiesToLog is != 0
-
-
BOOL skipInsert = FALSE;
ThreadStressLog* msgs = NULL;
msgs->Activate ();
- // We know this isn't going to throw an exception now because the call to ClrFlsSetValue above succeeded for
- // this thread.
- {
- CONTRACT_VIOLATION(ThrowsViolation);
- ClrFlsSetValue(theLog.TLSslot, msgs);
- }
+ t_pCurrentThreadLog = msgs;
if (!skipInsert) {
#ifdef _DEBUG
/*********************************************************************************/
/* static */
-void StressLog::ThreadDetach(ThreadStressLog *msgs) {
+void StressLog::ThreadDetach() {
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_FORBID_FAULT;
STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
+
#ifndef DACCESS_COMPILE
if (msgs == 0)
{
return;
}
- _ASSERTE(theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES); // because facilitiesToLog is != 0
+ t_pCurrentThreadLog = NULL;
+
// We are deleting a fiber. The thread is running a different fiber now.
// We should write this message to the StressLog for deleted fiber.
msgs->LogMsg (LF_STARTUP, 0, "******* DllMain THREAD_DETACH called Thread dying *******\n");
BOOL StressLog::ReserveStressLogChunks (unsigned chunksToReserve)
{
- ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot);
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
if (msgs == 0)
{
if(InlinedStressLogOn(facility, level))
{
- ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot);
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
if (msgs == 0) {
msgs = CreateThreadStressLog();
return argv;
}
-Volatile<PVOID> ForbidCallsIntoHostOnThisThread::s_pvOwningFiber = NULL;
-
//======================================================================
// This function returns true, if it can determine that the instruction pointer
// refers to a code address that belongs in the range of the given image.
#endif // FEATURE_CORRUPTING_EXCEPTIONS
-void EnableTerminationOnHeapCorruption()
-{
- HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
-}
-
namespace Clr
{
namespace Util
ThumbEmitLoadRegIndirect(dest, dest, offsetof(TEB, ThreadLocalStoragePointer));
- ThumbEmitLoadRegIndirect(dest, dest, sizeof(void *) * (g_TlsIndex & 0xFFFF));
+ ThumbEmitLoadRegIndirect(dest, dest, sizeof(void *) * _tls_index);
- ThumbEmitLoadRegIndirect(dest, dest, (g_TlsIndex & 0x7FFF0000) >> 16);
+ ThumbEmitLoadRegIndirect(dest, dest, (int)Thread::GetOffsetOfThreadStatic(&gCurrentThreadInfo));
#endif // TARGET_UNIX
}
HINSTANCE hInst;
DWORD dwReason;
LPVOID lpReserved;
- void **pTlsData;
} param;
param.hInst = hInst;
param.dwReason = dwReason;
param.lpReserved = lpReserved;
- param.pTlsData = NULL;
// Can't use PAL_TRY/EX_TRY here as they access the ClrDebugState which gets blown away as part of the
// PROCESS_DETACH path. Must use special PAL_TRY_FOR_DLLMAIN, passing the reason were in the DllMain.
// Don't destroy threads here if we're in shutdown (shutdown will
// clean up for us instead).
- // Store the TLS data; we'll need it later and we might NULL the slot in DetachThread.
- // This would be problematic because we can't depend on the FLS still existing.
- pParam->pTlsData = CExecutionEngine::CheckThreadStateNoCreate(0
-#ifdef _DEBUG
- // When we get here, OS has destroyed FLS, so FlsGetValue returns NULL now.
- // We have validation code in CExecutionEngine::CheckThreadStateNoCreate to ensure that
- // our TLS and FLS data are consistent, but since FLS has been destroyed, we need
- // to silent the check there. The extra arg for check build is for this purpose.
- , TRUE
-#endif
- );
Thread* thread = GetThread();
if (thread)
{
if (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH)
{
- CExecutionEngine::ThreadDetaching(param.pTlsData);
+ CExecutionEngine::ThreadDetaching();
}
return TRUE;
}
// Force shutdown of the EE
void ForceEEShutdown(ShutdownCompleteAction sca = SCA_ExitProcessWhenShutdownComplete);
+// Setup thread statics, including ClrDebugState and StressLog.
+void SetupTLSForThread(Thread* pThread);
+
// We have an internal class that can be used to expose EE functionality to other CLR
// DLLs, via the deliberately obscure IEE DLL exports from the shim and the EE
// NOTE: This class must not ever contain any instance variables. The reason for
public:
// Notification of a DLL_THREAD_DETACH or a Thread Terminate.
- static void ThreadDetaching(void **pTlsData);
-
- // Delete on TLS block
- static void DeleteTLS(void **pTlsData);
-
- static void **CheckThreadState(DWORD slot, BOOL force = TRUE);
- static void **CheckThreadStateNoCreate(DWORD slot
-#ifdef _DEBUG
- , BOOL fForDestruction = FALSE
-#endif // _DEBUG
- );
-
- // Setup FLS simulation block, including ClrDebugState and StressLog.
- static void SetupTLSForThread(Thread *pThread);
+ static void ThreadDetaching();
- static LPVOID* GetTlsData();
- static BOOL SetTlsData (void** ppTlsInfo);
-
- //***************************************************************************
- // private implementation:
- //***************************************************************************
private:
- static PTLS_CALLBACK_FUNCTION Callbacks[MAX_PREDEFINED_TLS_SLOT];
//***************************************************************************
// IUnknown methods
ULONG STDMETHODCALLTYPE Release();
//***************************************************************************
- // IExecutionEngine methods for TLS
- //***************************************************************************
-
- // Associate a callback for cleanup with a TLS slot
- VOID STDMETHODCALLTYPE TLS_AssociateCallback(
- DWORD slot,
- PTLS_CALLBACK_FUNCTION callback);
-
- // Get the TLS block for fast Get/Set operations
- LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
-
- // Get the value at a slot
- LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
-
- // Get the value at a slot, return FALSE if TLS info block doesn't exist
- BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
-
- // Set the value at a slot
- VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
-
- // Free TLS memory block and make callback
- VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
-
- //***************************************************************************
// IExecutionEngine methods for locking
//***************************************************************************
return pIL;
}
-PTR_COR_ILMETHOD ILCodeVersion::GetILNoThrow() const
-{
- LIMITED_METHOD_DAC_CONTRACT;
- PTR_COR_ILMETHOD ret;
- EX_TRY
- {
- ret = GetIL();
- }
- EX_CATCH
- {
- ret = NULL;
- }
- EX_END_CATCH(RethrowTerminalExceptions);
- return ret;
-}
-
DWORD ILCodeVersion::GetJitFlags() const
{
LIMITED_METHOD_DAC_CONTRACT;
bool HasAnyOptimizedNativeCodeVersion(NativeCodeVersion tier0NativeCodeVersion) const;
#endif
PTR_COR_ILMETHOD GetIL() const;
- PTR_COR_ILMETHOD GetILNoThrow() const;
DWORD GetJitFlags() const;
const InstrumentedILOffsetMapping* GetInstrumentedILMap() const;
typedef DPTR(PTR_OBJECTREF) PTR_PTR_OBJECTREF;
EXTERN_C Thread* STDCALL GetThread();
-BOOL SetThread(Thread*);
+void SetThread(Thread*);
// This is a mechanism by which macros can make the Thread pointer available to inner scopes
// that is robust to code changes. If the outer Thread no longer is available for some reason
_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)
+ if (pThread->HasStarted())
{
// Do not swallow the unhandled exception here
//
static BYTE g_CEEInstance[sizeof(CExecutionEngine)];
static Volatile<IExecutionEngine*> g_pCEE = NULL;
-PTLS_CALLBACK_FUNCTION CExecutionEngine::Callbacks[MAX_PREDEFINED_TLS_SLOT];
-
extern "C" IExecutionEngine * __stdcall IEE()
{
LIMITED_METHOD_CONTRACT;
return 1;
}
-struct ClrTlsInfo
-{
- void* data[MAX_PREDEFINED_TLS_SLOT];
-};
-
-#define DataToClrTlsInfo(a) ((ClrTlsInfo*)a)
-
-void** CExecutionEngine::GetTlsData()
-{
- LIMITED_METHOD_CONTRACT;
-
- return gCurrentThreadInfo.m_EETlsData;
-}
-
-BOOL CExecutionEngine::SetTlsData (void** ppTlsInfo)
-{
- LIMITED_METHOD_CONTRACT;
-
- gCurrentThreadInfo.m_EETlsData = ppTlsInfo;
- return TRUE;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Returns the current logical thread's data block (ClrTlsInfo::data).
-//
-// Arguments:
-// slot - Index of the slot that is about to be requested
-// force - If the data block does not exist yet, create it as a side-effect
-//
-// Return Value:
-// NULL, if the data block did not exist yet for the current thread and force was FALSE.
-// A pointer to the data block, otherwise.
-//
-// Notes:
-// If the underlying OS does not support fiber mode, the data block is stored in TLS.
-// If the underlying OS does support fiber mode, it is primarily stored in FLS,
-// and cached in TLS so that we can use our generated optimized TLS accessors.
-//
-// TLS support for the other DLLs of the CLR operates quite differently in hosted
-// and unhosted scenarios.
-
-void **CExecutionEngine::CheckThreadState(DWORD slot, BOOL force)
-{
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
- //<TODO> @TODO: Decide on an exception strategy for all the DLLs of the CLR, and then
- // enable all the exceptions out of this method.</TODO>
-
- // Treat as a runtime assertion, since the invariant spans many DLLs.
- _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT);
-// if (slot >= MAX_PREDEFINED_TLS_SLOT)
-// COMPlusThrow(kArgumentOutOfRangeException);
-
- void** pTlsData = CExecutionEngine::GetTlsData();
- BOOL fInTls = (pTlsData != NULL);
-
- ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData);
- if (pTlsInfo == 0 && force)
- {
-#undef HeapAlloc
-#undef GetProcessHeap
- // !!! Contract uses our TLS support. Contract may be used before our host support is set up.
- // !!! To better support contract, we call into OS for memory allocation.
- pTlsInfo = (ClrTlsInfo*) ::HeapAlloc(GetProcessHeap(),0,sizeof(ClrTlsInfo));
-#define GetProcessHeap() Dont_Use_GetProcessHeap()
-#define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes)
- if (pTlsInfo == NULL)
- {
- goto LError;
- }
- memset (pTlsInfo, 0, sizeof(ClrTlsInfo));
- }
-
- if (!fInTls && pTlsInfo)
- {
- if (!CExecutionEngine::SetTlsData(pTlsInfo->data))
- {
- goto LError;
- }
- }
-
- return pTlsInfo?pTlsInfo->data:NULL;
-
-LError:
- if (pTlsInfo)
- {
-#undef HeapFree
-#undef GetProcessHeap
- ::HeapFree(GetProcessHeap(), 0, pTlsInfo);
-#define GetProcessHeap() Dont_Use_GetProcessHeap()
-#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem)
- }
- // If this is for the stack probe, and we failed to allocate memory for it, we won't
- // put in a guard page.
- if (slot == TlsIdx_ClrDebugState)
- return NULL;
-
- ThrowOutOfMemory();
-}
-
-
-void **CExecutionEngine::CheckThreadStateNoCreate(DWORD slot
-#ifdef _DEBUG
- , BOOL fForDestruction
-#endif // _DEBUG
- )
-{
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_MODE_ANY;
-
- // !!! This function is called during Thread::SwitchIn and SwitchOut
- // !!! It is extremely important that while executing this function, we will not
- // !!! cause fiber switch. This means we can not allocate memory, lock, etc...
-
-
- // Treat as a runtime assertion, since the invariant spans many DLLs.
- _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT);
-
- void **pTlsData = CExecutionEngine::GetTlsData();
-
- ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData);
-
- return pTlsInfo?pTlsInfo->data:NULL;
-}
-
// Note: Sampling profilers also use this function to initialize TLS for a unmanaged
// sampling thread so that initialization can be done in advance to avoid deadlocks.
// See ProfToEEInterfaceImpl::InitializeCurrentThread for more details.
-void CExecutionEngine::SetupTLSForThread(Thread *pThread)
+void SetupTLSForThread(Thread* pThread)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_NOTRIGGER;
StressLog::CreateThreadStressLog();
}
#endif
- void **pTlsData;
- pTlsData = CheckThreadState(0);
- PREFIX_ASSUME(pTlsData != NULL);
+ // Make sure ThreadType can be seen by SOS
+ ClrFlsSetThreadType((TlsThreadTypeFlag)0);
#ifdef ENABLE_CONTRACTS
// Profilers need the side effect of GetClrDebugState() to perform initialization
// in advance to avoid deadlocks. Refer to ProfToEEInterfaceImpl::InitializeCurrentThread
- ClrDebugState *pDebugState = ::GetClrDebugState();
+ ClrDebugState* pDebugState = ::GetClrDebugState();
if (pThread)
pThread->m_pClrDebugState = pDebugState;
#endif
}
-static void ThreadDetachingHelper(PTLS_CALLBACK_FUNCTION callback, void* pData)
-{
- // Do not use contract. We are freeing TLS blocks.
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- callback(pData);
-}
+#ifdef ENABLE_CONTRACTS_IMPL
+// Fls callback to deallocate ClrDebugState when our FLS block goes away.
+void FreeClrDebugState(LPVOID pTlsData);
+#endif
-// Called here from a thread detach or from destruction of a Thread object. In
-// the detach case, we get our info from TLS. In the destruct case, it comes from
-// the object we are destructing.
-void CExecutionEngine::ThreadDetaching(void ** pTlsData)
+// Called here from a thread detach or from destruction of a Thread object.
+void CExecutionEngine::ThreadDetaching()
{
// Can not cause memory allocation during thread detach, so no real contracts.
STATIC_CONTRACT_NOTHROW;
// 2. When a fiber is destroyed, or OS calls FlsCallback after DLL_THREAD_DETACH process.
// We will null the FLS and TLS entry if it matches the deleted one.
- if (pTlsData)
- {
- DeleteTLS (pTlsData);
- }
-}
-
-void CExecutionEngine::DeleteTLS(void ** pTlsData)
-{
- // Can not cause memory allocation during thread detach, so no real contracts.
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- if (CExecutionEngine::GetTlsData() == NULL)
- {
- // We have not allocated TlsData yet.
- return;
- }
-
- PREFIX_ASSUME(pTlsData != NULL);
-
- ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData);
- BOOL fNeed;
- do
- {
- fNeed = FALSE;
- for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++)
- {
- if (i == TlsIdx_ClrDebugState ||
- i == TlsIdx_StressLog)
- {
- // StressLog and DebugState may be needed during callback.
- continue;
- }
- // If we have some data and a callback, issue it.
- if (Callbacks[i] != 0 && pTlsInfo->data[i] != 0)
- {
- void* pData = pTlsInfo->data[i];
- pTlsInfo->data[i] = 0;
- ThreadDetachingHelper(Callbacks[i], pData);
- fNeed = TRUE;
- }
- }
- } while (fNeed);
-
- if (pTlsInfo->data[TlsIdx_StressLog] != 0)
+ if (StressLog::t_pCurrentThreadLog != NULL)
{
#ifdef STRESS_LOG
- StressLog::ThreadDetach((ThreadStressLog *)pTlsInfo->data[TlsIdx_StressLog]);
+ StressLog::ThreadDetach();
#else
_ASSERTE (!"should not have StressLog");
#endif
}
- if (Callbacks[TlsIdx_ClrDebugState] != 0 && pTlsInfo->data[TlsIdx_ClrDebugState] != 0)
- {
- void* pData = pTlsInfo->data[TlsIdx_ClrDebugState];
- pTlsInfo->data[TlsIdx_ClrDebugState] = 0;
- ThreadDetachingHelper(Callbacks[TlsIdx_ClrDebugState], pData);
- }
-
- // NULL TLS and FLS entry so that we don't double free.
- // We may get two callback here on thread death
- // 1. From EEDllMain
- // 2. From OS callback on FLS destruction
- if (CExecutionEngine::GetTlsData() == pTlsData)
- {
- CExecutionEngine::SetTlsData(0);
- }
-
-#undef HeapFree
-#undef GetProcessHeap
- ::HeapFree (GetProcessHeap(),0,pTlsInfo);
-#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem)
-#define GetProcessHeap() Dont_Use_GetProcessHeap()
-
-}
-
#ifdef ENABLE_CONTRACTS_IMPL
-// Fls callback to deallocate ClrDebugState when our FLS block goes away.
-void FreeClrDebugState(LPVOID pTlsData);
-#endif
-
-VOID STDMETHODCALLTYPE CExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
-{
- WRAPPER_NO_CONTRACT;
-
- CheckThreadState(slot);
-
- // They can toggle between a callback and no callback. But anything else looks like
- // confusion on their part.
- //
- // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But
- // all the callbacks are equally good.)
- _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0);
- if (slot == TlsIdx_ClrDebugState)
+ if (t_pClrDebugState != NULL)
{
-#ifdef ENABLE_CONTRACTS_IMPL
- // ClrDebugState is shared among many dlls. Some dll, like perfcounter.dll, may be unloaded.
- // We force the callback function to be in mscorwks.dll.
- Callbacks[slot] = FreeClrDebugState;
-#else
- _ASSERTE (!"should not get here");
-#endif
+ void* pData = t_pClrDebugState;
+ t_pClrDebugState = 0;
+ FreeClrDebugState(pData);
}
- else
- Callbacks[slot] = callback;
+#endif // ENABLE_CONTRACTS_IMPL
}
-LPVOID* STDMETHODCALLTYPE CExecutionEngine::TLS_GetDataBlock()
-{
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
- return CExecutionEngine::GetTlsData();
-}
-
-LPVOID STDMETHODCALLTYPE CExecutionEngine::TLS_GetValue(DWORD slot)
-{
- WRAPPER_NO_CONTRACT;
- return EETlsGetValue(slot);
-}
-
-BOOL STDMETHODCALLTYPE CExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue)
-{
- WRAPPER_NO_CONTRACT;
- return EETlsCheckValue(slot, pValue);
-}
-
-VOID STDMETHODCALLTYPE CExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData)
-{
- WRAPPER_NO_CONTRACT;
- EETlsSetValue(slot,pData);
-}
-
-
-VOID STDMETHODCALLTYPE CExecutionEngine::TLS_ThreadDetaching()
-{
- WRAPPER_NO_CONTRACT;
- CExecutionEngine::ThreadDetaching(NULL);
-}
-
-
CRITSEC_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
{
CONTRACTL
return E_FAIL;
}
+DWORD GetCurrentExceptionCode()
+{
+ return 0;
+}
+
//---------------------------------------------------------------------------------------
//
// Dynamically unreachable implementation of profiler callbacks. Note that we can't just
}
CONTRACTL_END;
- if (
- (((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & (ThreadType_Finalizer|ThreadType_DbgHelper|ThreadType_Shutdown|ThreadType_GC)) == 0)
+ if ((t_ThreadType & (ThreadType_Finalizer|ThreadType_DbgHelper|ThreadType_Shutdown|ThreadType_GC)) == 0)
{
// The process is shutting down. Release the lock and just block forever.
this->Leave();
#ifdef _DEBUG
+
+thread_local CrstBase* t_pOwnedCrstsChain;
+
void CrstBase::PreEnter()
{
STATIC_CONTRACT_NOTHROW;
_ASSERTE((m_next == NULL) && (m_prev == NULL));
// Link this Crst into the Thread's chain of OwnedCrsts
- CrstBase *pcrst = GetThreadsOwnedCrsts();
+ CrstBase *pcrst = t_pOwnedCrstsChain;
if (pcrst == NULL)
{
- SetThreadsOwnedCrsts (this);
+ t_pOwnedCrstsChain = this;
}
else
{
if (m_prev)
m_prev->m_next = m_next;
else
- SetThreadsOwnedCrsts(m_next);
+ t_pOwnedCrstsChain = m_next;
if (m_next)
m_next->m_prev = m_prev;
const int crstDebugInfoCount = 4000;
CrstDebugInfo crstDebugInfo[crstDebugInfoCount];
-CrstBase *CrstBase::GetThreadsOwnedCrsts()
-{
- return (CrstBase*)ClrFlsGetValue(TlsIdx_OwnedCrstsChain);
-}
-void CrstBase::SetThreadsOwnedCrsts(CrstBase *pCrst)
-{
- WRAPPER_NO_CONTRACT;
- ClrFlsSetValue(TlsIdx_OwnedCrstsChain, (LPVOID) (pCrst));
-}
-
void CrstBase::DebugInit(CrstType crstType, CrstFlags flags)
{
LIMITED_METHOD_CONTRACT;
{
if (m_next)
m_next->m_prev = NULL; // workaround: break up the chain
- SetThreadsOwnedCrsts(NULL);
+ t_pOwnedCrstsChain = NULL;
}
}
else
// See if the current thread already owns a lower or sibling lock.
BOOL fSafe = TRUE;
- for (CrstBase *pcrst = GetThreadsOwnedCrsts(); pcrst != NULL; pcrst = pcrst->m_next)
+ for (CrstBase *pcrst = t_pOwnedCrstsChain; pcrst != NULL; pcrst = pcrst->m_next)
{
fSafe =
!pcrst->m_holderthreadid.IsCurrentThread()
#endif
}
- CrstBase *GetThreadsOwnedCrsts();
- void SetThreadsOwnedCrsts(CrstBase *pCrst);
-
NOINLINE EEThreadId GetHolderThreadId()
{
LIMITED_METHOD_CONTRACT;
virtual bool SweepThreadsForDebug(bool forceSync) = 0;
virtual void GetRuntimeOffsets(SIZE_T *pTLSIndex,
- SIZE_T *pTLSIsSpecialIndex,
- SIZE_T *pTLSCantStopIndex,
+ SIZE_T *pTLSEEThreadOffset,
+ SIZE_T *pTLSIsSpecialOffset,
+ SIZE_T *pTLSCantStopOffset,
SIZE_T *pEEThreadStateOffset,
SIZE_T *pEEThreadStateNCOffset,
SIZE_T *pEEThreadPGCDisabledOffset,
DWORD *pEEThreadSteppingStateMask,
DWORD *pEEMaxFrameValue,
SIZE_T *pEEThreadDebuggerFilterContextOffset,
- SIZE_T *pEEThreadCantStopMask,
SIZE_T *pEEFrameNextOffset,
DWORD *pEEIsManagedExceptionStateMask) = 0;
thread->DecrementTraceCallCount();
}
-EXTERN_C UINT32 _tls_index;
-
void EEDbgInterfaceImpl::GetRuntimeOffsets(SIZE_T *pTLSIndex,
- SIZE_T *pTLSIsSpecialIndex,
- SIZE_T *pTLSCantStopIndex,
+ SIZE_T *pTLSEEThreadOffset,
+ SIZE_T *pTLSIsSpecialOffset,
+ SIZE_T *pTLSCantStopOffset,
SIZE_T *pEEThreadStateOffset,
SIZE_T *pEEThreadStateNCOffset,
SIZE_T *pEEThreadPGCDisabledOffset,
DWORD *pEEThreadSteppingStateMask,
DWORD *pEEMaxFrameValue,
SIZE_T *pEEThreadDebuggerFilterContextOffset,
- SIZE_T *pEEThreadCantStopOffset,
SIZE_T *pEEFrameNextOffset,
DWORD *pEEIsManagedExceptionStateMask)
{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- PRECONDITION(CheckPointer(pTLSIndex));
- PRECONDITION(CheckPointer(pTLSIsSpecialIndex));
- PRECONDITION(CheckPointer(pEEThreadStateOffset));
- PRECONDITION(CheckPointer(pEEThreadStateNCOffset));
- PRECONDITION(CheckPointer(pEEThreadPGCDisabledOffset));
- PRECONDITION(CheckPointer(pEEThreadPGCDisabledValue));
- PRECONDITION(CheckPointer(pEEThreadFrameOffset));
- PRECONDITION(CheckPointer(pEEThreadMaxNeededSize));
- PRECONDITION(CheckPointer(pEEThreadSteppingStateMask));
- PRECONDITION(CheckPointer(pEEMaxFrameValue));
- PRECONDITION(CheckPointer(pEEThreadDebuggerFilterContextOffset));
- PRECONDITION(CheckPointer(pEEThreadCantStopOffset));
- PRECONDITION(CheckPointer(pEEFrameNextOffset));
- PRECONDITION(CheckPointer(pEEIsManagedExceptionStateMask));
- }
- CONTRACTL_END;
+ LIMITED_METHOD_CONTRACT;
- *pTLSIndex = g_TlsIndex;
- *pTLSIsSpecialIndex = TlsIdx_ThreadType;
- *pTLSCantStopIndex = TlsIdx_CantStopCount;
+#ifdef TARGET_WINDOWS
+ *pTLSIndex = _tls_index;
+ *pTLSEEThreadOffset = Thread::GetOffsetOfThreadStatic(&gCurrentThreadInfo.m_pThread);
+ *pTLSIsSpecialOffset = Thread::GetOffsetOfThreadStatic(&t_ThreadType);
+ *pTLSCantStopOffset = Thread::GetOffsetOfThreadStatic(&t_CantStopCount);
+#else
+ *pTLSIndex = (SIZE_T)-1;
+ *pTLSEEThreadOffset = (SIZE_T)-1;
+ *pTLSIsSpecialOffset = (SIZE_T)-1;
+ *pTLSCantStopOffset = (SIZE_T)-1;
+#endif
*pEEThreadStateOffset = Thread::GetOffsetOfState();
*pEEThreadStateNCOffset = Thread::GetOffsetOfStateNC();
*pEEThreadPGCDisabledOffset = Thread::GetOffsetOfGCFlag();
*pEEThreadFrameOffset = Thread::GetOffsetOfCurrentFrame();
*pEEThreadMaxNeededSize = sizeof(Thread);
*pEEThreadDebuggerFilterContextOffset = Thread::GetOffsetOfDebuggerFilterContext();
- *pEEThreadCantStopOffset = Thread::GetOffsetOfCantStop();
*pEEThreadSteppingStateMask = Thread::TSNC_DebuggerIsStepping;
*pEEMaxFrameValue = (DWORD)(size_t)FRAME_TOP; // <TODO> should this be size_t for 64bit?</TODO>
*pEEFrameNextOffset = Frame::GetOffsetOfNextLink();
void DisableTraceCall(Thread *thread);
void GetRuntimeOffsets(SIZE_T *pTLSIndex,
- SIZE_T *pTLSIsSpecialIndex,
- SIZE_T *pTLSCantStopIndex,
+ SIZE_T *pTLSEEThreadOffset,
+ SIZE_T *pTLSIsSpecialOffset,
+ SIZE_T *pTLSCantStopOffset,
SIZE_T *pEEThreadStateOffset,
SIZE_T *pEEThreadStateNCOffset,
SIZE_T *pEEThreadPGCDisabledOffset,
DWORD *pEEThreadSteppingStateMask,
DWORD *pEEMaxFrameValue,
SIZE_T *pEEThreadDebuggerFilterContextOffset,
- SIZE_T *pEEThreadCantStopOffset,
SIZE_T *pEEFrameNextOffset,
DWORD *pEEIsManagedExceptionStateMask);
RaiseTheExceptionInternalOnly(orThrowable, FALSE);
}
+thread_local DWORD t_dwCurrentExceptionCode;
+thread_local PEXCEPTION_RECORD t_pCurrentExceptionRecord;
+thread_local PCONTEXT t_pCurrentExceptionContext;
+
+void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo DEBUG_ARG(bool checkExceptionRecordLocation))
+{
+ WRAPPER_NO_CONTRACT;
+
+ pExceptionInfo->ContextRecord = t_pCurrentExceptionContext;
+ pExceptionInfo->ExceptionRecord = t_pCurrentExceptionRecord;
+
+#ifdef _DEBUG
+ if (pExceptionInfo->ExceptionRecord != NULL && checkExceptionRecordLocation)
+ {
+ _ASSERTE((PVOID)(pExceptionInfo->ExceptionRecord) > (PVOID)(&checkExceptionRecordLocation));
+ }
+#endif
+}
+
+DWORD GetCurrentExceptionCode()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ return t_dwCurrentExceptionCode;
+}
+
void SaveCurrentExceptionInfo(PEXCEPTION_RECORD pRecord, PCONTEXT pContext)
{
CONTRACTL
return;
}
- if (CExecutionEngine::CheckThreadStateNoCreate(TlsIdx_PEXCEPTION_RECORD))
+ BOOL fSave = TRUE;
+ if (pRecord->ExceptionCode != STATUS_STACK_OVERFLOW)
{
- BOOL fSave = TRUE;
- if (pRecord->ExceptionCode != STATUS_STACK_OVERFLOW)
+ DWORD dwLastExceptionCode = t_dwCurrentExceptionCode;
+ if (dwLastExceptionCode == STATUS_STACK_OVERFLOW)
{
- DWORD dwLastExceptionCode = (DWORD)(SIZE_T) (ClrFlsGetValue(TlsIdx_EXCEPTION_CODE));
- if (dwLastExceptionCode == STATUS_STACK_OVERFLOW)
- {
- PEXCEPTION_RECORD lastRecord =
- static_cast<PEXCEPTION_RECORD> (ClrFlsGetValue(TlsIdx_PEXCEPTION_RECORD));
+ PEXCEPTION_RECORD lastRecord = t_pCurrentExceptionRecord;
- // We are trying to see if C++ is attempting a rethrow of a SO exception. If so,
- // we want to prevent updating the exception details in the TLS. This is a workaround,
- // as explained below.
- if (pRecord->ExceptionCode == EXCEPTION_MSVC)
+ // We are trying to see if C++ is attempting a rethrow of a SO exception. If so,
+ // we want to prevent updating the exception details in the TLS. This is a workaround,
+ // as explained below.
+ if (pRecord->ExceptionCode == EXCEPTION_MSVC)
+ {
+ // This is a workaround.
+ // When C++ rethrows, C++ internally gets rid of the new exception record after
+ // unwinding stack, and present the original exception record to the thread.
+ // When we get VC's support to obtain exception record in try/catch, we will replace
+ // this code.
+ if (pRecord < lastRecord)
{
- // This is a workaround.
- // When C++ rethrows, C++ internally gets rid of the new exception record after
- // unwinding stack, and present the original exception record to the thread.
- // When we get VC's support to obtain exception record in try/catch, we will replace
- // this code.
- if (pRecord < lastRecord)
+ // For the C++ rethrow workaround, ensure that the last exception record is still valid and as we expect it to be.
+ //
+ // Its possible that we are still below the address of last exception record
+ // but since the execution stack could have changed, simply comparing its address
+ // with the address of the current exception record may not be enough.
+ //
+ // Thus, ensure that its still valid and holds the exception code we expect it to
+ // have (i.e. value in dwLastExceptionCode).
+ if ((lastRecord != NULL) && (lastRecord->ExceptionCode == dwLastExceptionCode))
{
- // For the C++ rethrow workaround, ensure that the last exception record is still valid and as we expect it to be.
- //
- // Its possible that we are still below the address of last exception record
- // but since the execution stack could have changed, simply comparing its address
- // with the address of the current exception record may not be enough.
- //
- // Thus, ensure that its still valid and holds the exception code we expect it to
- // have (i.e. value in dwLastExceptionCode).
- if ((lastRecord != NULL) && (lastRecord->ExceptionCode == dwLastExceptionCode))
- {
- fSave = FALSE;
- }
+ fSave = FALSE;
}
}
}
}
- if (fSave)
- {
- ClrFlsSetValue(TlsIdx_EXCEPTION_CODE, (void*)(size_t)(pRecord->ExceptionCode));
- ClrFlsSetValue(TlsIdx_PEXCEPTION_RECORD, pRecord);
- ClrFlsSetValue(TlsIdx_PCONTEXT, pContext);
- }
+ }
+ if (fSave)
+ {
+ t_dwCurrentExceptionCode = pRecord->ExceptionCode;
+ t_pCurrentExceptionRecord = pRecord;
+ t_pCurrentExceptionContext = pContext;
}
}
&& GetExecutionEngine ()
// If we have not created StressLog ring buffer, we should not try to use it.
// StressLog is going to do a memory allocation. We may enter an endless loop.
- && ClrFlsGetValue(TlsIdx_StressLog) != NULL )
+ && StressLog::t_pCurrentThreadLog != NULL )
{
STRESS_LOG_OOM_STACK(dwBytes);
}
pCrst->Leave();
}
-LPVOID EETlsGetValue(DWORD slot)
-{
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_MODE_ANY;
- STATIC_CONTRACT_CANNOT_TAKE_LOCK;
-
- //
- // @todo: we don't want TlsGetValue to throw, but CheckThreadState throws right now. Either modify
- // CheckThreadState to not throw, or catch any exception and just return NULL.
- //
- //CONTRACT_VIOLATION(ThrowsViolation);
- SCAN_IGNORE_THROW;
-
- void **pTlsData = CExecutionEngine::CheckThreadState(slot, FALSE);
-
- if (pTlsData)
- return pTlsData[slot];
- else
- return NULL;
-}
-
-BOOL EETlsCheckValue(DWORD slot, LPVOID * pValue)
-{
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_MODE_ANY;
-
- //
- // @todo: we don't want TlsGetValue to throw, but CheckThreadState throws right now. Either modify
- // CheckThreadState to not throw, or catch any exception and just return NULL.
- //
- //CONTRACT_VIOLATION(ThrowsViolation);
- SCAN_IGNORE_THROW;
-
- void **pTlsData = CExecutionEngine::CheckThreadState(slot, FALSE);
-
- if (pTlsData)
- {
- *pValue = pTlsData[slot];
- return TRUE;
- }
-
- return FALSE;
-}
-
-VOID EETlsSetValue(DWORD slot, LPVOID pData)
-{
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_MODE_ANY;
-
- void **pTlsData = CExecutionEngine::CheckThreadState(slot);
-
- if (pTlsData) // Yes, CheckThreadState(slot, TRUE) can return NULL now.
- {
- pTlsData[slot] = pData;
- }
-}
-
BOOL EEAllocationDisallowed()
{
WRAPPER_NO_CONTRACT;
DWORD EESleepEx(DWORD dwMilliseconds, BOOL bAlertable);
-// TLS functions
-LPVOID EETlsGetValue(DWORD slot);
-BOOL EETlsCheckValue(DWORD slot, LPVOID * pValue);
-VOID EETlsSetValue(DWORD slot, LPVOID pData);
-
-
-
#endif
EmitBytes(code, sizeof(code));
Emit32(offsetof(TEB, ThreadLocalStoragePointer));
- X86EmitIndexRegLoad(dstreg, dstreg, sizeof(void *) * (g_TlsIndex & 0xFFFF));
+ X86EmitIndexRegLoad(dstreg, dstreg, sizeof(void *) * _tls_index);
- X86EmitIndexRegLoad(dstreg, dstreg, (g_TlsIndex & 0x7FFF0000) >> 16);
+ X86EmitIndexRegLoad(dstreg, dstreg, (int)Thread::GetOffsetOfThreadStatic(&gCurrentThreadInfo));
#endif // TARGET_UNIX
}
ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
// Mark that we are performing a stackwalker like operation on the current thread.
// This is necessary to allow the signature parsing functions to work without triggering any loads
- ClrFlsValueSwitch threadStackWalking(TlsIdx_StackWalkerWalkingThread, GetThread());
+ StackWalkerWalkingThreadHolder threadStackWalking(GetThread());
#ifdef TARGET_X86
MetaSig msig(this);
// Remember that we're walking the stack. This holder will reinstate the original
// value of the stackwalker flag (from the thread type mask) in its destructor.
- ClrFlsValueSwitch _threadStackWalking(TlsIdx_StackWalkerWalkingThread, pThreadToSnapshot);
+ StackWalkerWalkingThreadHolder threadStackWalking(pThreadToSnapshot);
// This flag remembers if we reported a managed frame since the last unmanaged block
// we reported. It's used to avoid reporting two unmanaged blocks in a row.
LL_INFO10,
"**PROF: InitializeCurrentThread.\n"));
- HRESULT hr = S_OK;
-
- EX_TRY
- {
- CExecutionEngine::SetupTLSForThread(GetThread());
- }
- EX_CATCH_HRESULT(hr);
+ SetupTLSForThread(GetThread());
- if (FAILED(hr))
- return hr;
-
- return S_OK;
+ return S_OK;
}
struct InternalProfilerModuleEnum : public ProfilerModuleEnum
{
// SCOPE: Remember that we're walking the stack.
//
- // Normally, we'd use a holder (ClrFlsThreadTypeSwitch) to temporarily set this
+ // Normally, we'd use a StackWalkerWalkingThreadHolder to temporarily set this
// flag in the thread state, but we can't in this function, since C++ destructors
// are forbidden when this is called for exception handling (which causes
// MakeStackwalkerCallback() not to return). Note that in exception handling
// cases, we will have already cleared the stack walker thread state indicator inside
// MakeStackwalkerCallback(), so we will be properly cleaned up.
#if !defined(DACCESS_COMPILE)
- PVOID pStackWalkThreadOrig = ClrFlsGetValue(TlsIdx_StackWalkerWalkingThread);
+ Thread* pStackWalkThreadOrig = t_pStackWalkerWalkingThread;
#endif
SET_THREAD_TYPE_STACKWALKER(this);
_ASSERTE(pThread != NULL);
_ASSERTE(pRegDisp != NULL);
-#if !defined(DACCESS_COMPILE)
- // When the LIGHTUNWIND flag is set, we use the stack walk cache.
- // On x64, accesses to the stack walk cache are synchronized by
- // a CrstStatic, which may need to call back into the host.
- _ASSERTE(CanThisThreadCallIntoHost() || (flags & LIGHTUNWIND) == 0);
-#endif // DACCESS_COMPILE
-
#ifdef FEATURE_EH_FUNCLETS
_ASSERTE(!(flags & POPFRAMES));
_ASSERTE(pRegDisp->pCurrentContext);
#include "eventpipebuffermanager.h"
#endif // FEATURE_PERFTRACING
+#if defined (_DEBUG_IMPL) || defined(_PREFAST_)
+thread_local int t_ForbidGCLoaderUseCount;
+#endif
+
uint64_t Thread::dead_threads_non_alloc_bytes = 0;
SPTR_IMPL(ThreadStore, ThreadStore, s_pThreadStore);
CrstStatic g_DeadlockAwareCrst;
+//
+// A transient thread value that indicates this thread is currently walking its stack
+// or the stack of another thread. This value is useful to help short-circuit
+// some problematic checks in the loader, guarantee that types & assemblies
+// encountered during the walk must already be loaded, and provide information to control
+// assembly loading behavior during stack walks.
+//
+// This value is set around the main portions of the stack walk (as those portions may
+// enter the type & assembly loaders). This is also explicitly cleared while the
+// walking thread calls the stackwalker callback or needs to execute managed code, as
+// such calls may execute arbitrary code unrelated to the actual stack walking, and
+// may never return, in the case of exception stackwalk callbacks.
+//
+thread_local Thread* t_pStackWalkerWalkingThread;
#if defined(_DEBUG)
BOOL MatchThreadHandleToOsId ( HANDLE h, DWORD osId )
__thread ThreadLocalInfo gCurrentThreadInfo;
#endif
-// index into TLS Array. Definition added by compiler
-EXTERN_C UINT32 _tls_index;
-
-#ifndef HOST_WINDOWS
-UINT32 _tls_index;
-#endif
-
#ifndef DACCESS_COMPILE
-BOOL SetThread(Thread* t)
+void SetThread(Thread* t)
{
LIMITED_METHOD_CONTRACT
gCurrentThreadInfo.m_pThread = t;
- return TRUE;
}
-BOOL SetAppDomain(AppDomain* ad)
+void SetAppDomain(AppDomain* ad)
{
LIMITED_METHOD_CONTRACT
gCurrentThreadInfo.m_pAppDomain = ad;
- return TRUE;
}
BOOL Thread::Alert ()
Holder<Thread*,DoNothing<Thread*>,DeleteThread> threadHolder(pThread);
- CExecutionEngine::SetupTLSForThread(pThread);
+ SetupTLSForThread(pThread);
// A host can deny a thread entering runtime by returning a NULL IHostTask.
// But we do want threads used by threadpool.
ThreadStore::AddThread(pThread);
- BOOL fOK = SetThread(pThread);
- _ASSERTE (fOK);
- fOK = SetAppDomain(pThread->GetDomain());
- _ASSERTE (fOK);
+ SetThread(pThread);
+ SetAppDomain(pThread->GetDomain());
#ifdef FEATURE_INTEROP_DEBUGGING
// Ensure that debugger word slot is allocated
{
LIMITED_METHOD_CONTRACT;
+#ifdef HOST_WINDOWS
return _tls_index;
+#else
+ return 0;
+#endif
}
//---------------------------------------------------------------------------
extern "C" void *JIT_WriteBarrier_Loc;
+#ifndef TARGET_UNIX
+// g_TlsIndex is only used by the DAC. Disable optimizations around it to prevent it from getting optimized out.
+#pragma optimize("", off)
+static void SetIlsIndex(DWORD tlsIndex)
+{
+ g_TlsIndex = tlsIndex;
+}
+#pragma optimize("", on)
+#endif
+
//---------------------------------------------------------------------------
// One-time initialization. Called during Dll initialization. So
// be careful what you do in here!
#ifndef TARGET_UNIX
_ASSERTE(GetThread() == NULL);
- PTEB Teb = NtCurrentTeb();
- BYTE** tlsArray = (BYTE**)Teb->ThreadLocalStoragePointer;
- BYTE* tlsData = (BYTE*)tlsArray[_tls_index];
-
- size_t offsetOfCurrentThreadInfo = (BYTE*)&gCurrentThreadInfo - tlsData;
+ size_t offsetOfCurrentThreadInfo = Thread::GetOffsetOfThreadStatic(&gCurrentThreadInfo);
_ASSERTE(offsetOfCurrentThreadInfo < 0x8000);
_ASSERTE(_tls_index < 0x10000);
// Save gCurrentThreadInfo location for debugger
- g_TlsIndex = (DWORD)(_tls_index + (offsetOfCurrentThreadInfo << 16) + 0x80000000);
+ SetIlsIndex((DWORD)(_tls_index + (offsetOfCurrentThreadInfo << 16) + 0x80000000));
_ASSERTE(g_TrapReturningThreads == 0);
#endif // !TARGET_UNIX
COMPlusThrowWin32();
#endif
- __ClrFlsGetBlock = CExecutionEngine::GetTlsData;
-
IfFailThrow(Thread::CLRSetThreadStackGuarantee(Thread::STSGuarantee_Force));
ThreadStore::InitThreadStore();
m_ltoIsUnhandled = FALSE;
m_debuggerFilterContext = NULL;
- m_debuggerCantStop = 0;
m_fInteropDebuggingHijacked = FALSE;
m_profilerCallbackState = 0;
#if defined(PROFILING_SUPPORTED) || defined(PROFILING_SUPPORTED_DATA)
//
// Initialization must happen in the following order - hosts like SQL Server depend on this.
//
- CExecutionEngine::SetupTLSForThread(this);
+
+ SetupTLSForThread(this);
fCanCleanupCOMState = TRUE;
res = PrepareApartmentAndContext();
InitThread(FALSE);
- if (SetThread(this) == FALSE)
- {
- ThrowOutOfMemory();
- }
-
- if (SetAppDomain(m_pDomain) == FALSE)
- {
- ThrowOutOfMemory();
- }
+ SetThread(this);
+ SetAppDomain(m_pDomain);
ThreadStore::TransferStartedThread(this, bRequiresTSL);
{
// The thread pointer in TLS may not be set yet, if we had a failure before we set it.
// So we'll set it up here (we'll unset it a few lines down).
- if (SetThread(this) != FALSE)
- {
- CleanupCOMState();
- }
+ SetThread(this);
+ CleanupCOMState();
}
#endif
FastInterlockDecrement(&ThreadStore::s_pThreadStore->m_PendingThreadCount);
#ifndef DACCESS_COMPILE
-// @todo - eventually complete remove the CantStop count on the thread and use
-// the one in the PreDef block. For now, we increment both our thread counter,
-// and the FLS counter. Eventually we can remove our thread counter and only use
-// the FLS counter.
-void Thread::SetDebugCantStop(bool fCantStop)
-{
- LIMITED_METHOD_CONTRACT;
-
- if (fCantStop)
- {
- IncCantStopCount();
- m_debuggerCantStop++;
- }
- else
- {
- DecCantStopCount();
- m_debuggerCantStop--;
- }
-}
-
-// @todo - remove this, we only read this from oop.
-bool Thread::GetDebugCantStop(void)
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_debuggerCantStop != 0;
-}
-
void Thread::InitContext()
{
CONTRACTL {
// of that physical thread.
// FEATURE_MULTIREG_RETURN is set for platforms where a struct return value
-// [GcInfo v2 only] can be returned in multiple registers
+// can be returned in multiple registers
// ex: Windows/Unix ARM/ARM64, Unix-AMD64.
//
//
// UNIX_AMD64_ABI is a specific kind of FEATURE_MULTIREG_RETURN
-// [GcInfo v1 and v2] specified by SystemV ABI for AMD64
+// specified by SystemV ABI for AMD64
//
#ifdef FEATURE_HIJACK // Hijack function returning
friend class DacDbiInterfaceImpl; // DacDbiInterfaceImpl::GetThreadHandle(HANDLE * phThread);
#endif // DACCESS_COMPILE
friend class ProfToEEInterfaceImpl; // HRESULT ProfToEEInterfaceImpl::GetHandleFromThread(ThreadID threadId, HANDLE *phThread);
- friend class CExecutionEngine;
+ friend void SetupTLSForThread(Thread* pThread);
friend class UnC;
friend class CheckAsmOffsets;
#endif // ENABLE_CONTRACTS_IMPL
//---------------------------------------------------------------
+ // Calculate thread static offset
+ //---------------------------------------------------------------
+ static size_t GetOffsetOfThreadStatic(void* pThreadStatic);
+
+ //---------------------------------------------------------------
// Expose key offsets and values for stub generation.
//---------------------------------------------------------------
static BYTE GetOffsetOfCurrentFrame()
return (BYTE)ofs;
}
- static void StaticDisablePreemptiveGC( Thread *pThread)
- {
- WRAPPER_NO_CONTRACT;
- _ASSERTE(pThread != NULL);
- pThread->DisablePreemptiveGC();
- }
-
- static void StaticEnablePreemptiveGC( Thread *pThread)
- {
- WRAPPER_NO_CONTRACT;
- _ASSERTE(pThread != NULL);
- pThread->EnablePreemptiveGC();
- }
-
-
- //---------------------------------------------------------------
- // Expose offset of the app domain word for the interop and delegate callback
- //---------------------------------------------------------------
- static SIZE_T GetOffsetOfAppDomain()
- {
- LIMITED_METHOD_CONTRACT;
- return (SIZE_T)(offsetof(class Thread, m_pDomain));
- }
-
//---------------------------------------------------------------
// Expose offset of the place for storing the filter context for the debugger.
//---------------------------------------------------------------
}
//---------------------------------------------------------------
- // Expose offset of the debugger cant stop count for the debugger
- //---------------------------------------------------------------
- static SIZE_T GetOffsetOfCantStop()
- {
- LIMITED_METHOD_CONTRACT;
- return (SIZE_T)(offsetof(class Thread, m_debuggerCantStop));
- }
-
- //---------------------------------------------------------------
// Expose offset of m_StateNC
//---------------------------------------------------------------
static SIZE_T GetOffsetOfStateNC()
// return addresses on the stack)
//---------------------------------------------------------------
Volatile<LONG> m_hijackLock;
- //---------------------------------------------------------------
- // m_debuggerCantStop holds a count of entries into "can't stop"
- // areas that the Interop Debugging Services must know about.
- //---------------------------------------------------------------
- DWORD m_debuggerCantStop;
//---------------------------------------------------------------
// The current custom notification data object (or NULL if none
ResetThreadState(TS_GCSuspendPending);
}
- void SetDebugCantStop(bool fCantStop);
- bool GetDebugCantStop(void);
-
static LPVOID GetStaticFieldAddress(FieldDesc *pFD);
TADDR GetStaticFieldAddrNoCreate(FieldDesc *pFD);
#endif // FEATURE_COMINTEROP
private:
- // This duplicates the ThreadType_GC bit stored in TLS (TlsIdx_ThreadType). It exists
+ // This duplicates the ThreadType_GC bit stored in TLS (t_ThreadType). It exists
// so that any thread can query whether any other thread is a "GC Special" thread.
// (In contrast, ::IsGCSpecialThread() only gives this info about the currently
// executing thread.) The Profiling API uses this to determine whether it should
#endif // _DEBUG
#ifdef _DEBUG_IMPL
+
+extern thread_local int t_ForbidGCLoaderUseCount;
+
// Holder for incrementing the ForbidGCLoaderUse counter.
class GCForbidLoaderUseHolder
{
GCForbidLoaderUseHolder()
{
WRAPPER_NO_CONTRACT;
- ClrFlsIncrementValue(TlsIdx_ForbidGCLoaderUseCount, 1);
+ t_ForbidGCLoaderUseCount++;
}
~GCForbidLoaderUseHolder()
{
WRAPPER_NO_CONTRACT;
- ClrFlsIncrementValue(TlsIdx_ForbidGCLoaderUseCount, -1);
+ t_ForbidGCLoaderUseCount--;
}
};
#define FORBIDGC_LOADER_USE_ENABLED() true
#else // DACCESS_COMPILE
-#if defined (_DEBUG_IMPL) || defined(_PREFAST_)
-#ifndef DACCESS_COMPILE
-#define FORBIDGC_LOADER_USE_ENABLED() (ClrFlsGetValue(TlsIdx_ForbidGCLoaderUseCount))
-#else
-#define FORBIDGC_LOADER_USE_ENABLED() TRUE
-#endif
+#ifdef _DEBUG_IMPL
+#define FORBIDGC_LOADER_USE_ENABLED() (t_ForbidGCLoaderUseCount)
#else // _DEBUG_IMPL
// If you got an error about FORBIDGC_LOADER_USE_ENABLED being undefined, it's because you tried
#endif // FEATURE_WRITEBARRIER_COPY
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+extern thread_local Thread* t_pStackWalkerWalkingThread;
+#define SET_THREAD_TYPE_STACKWALKER(pThread) t_pStackWalkerWalkingThread = pThread
+#define CLEAR_THREAD_TYPE_STACKWALKER() t_pStackWalkerWalkingThread = NULL
+#else
+#define SET_THREAD_TYPE_STACKWALKER(pThread)
+#define CLEAR_THREAD_TYPE_STACKWALKER()
+#endif
+
+inline BOOL IsStackWalkerThread()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+ return t_pStackWalkerWalkingThread != NULL;
+#else
+ return FALSE;
+#endif
+}
+
+class StackWalkerWalkingThreadHolder
+{
+public:
+ StackWalkerWalkingThreadHolder(Thread* value)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+ m_PreviousValue = t_pStackWalkerWalkingThread;
+ t_pStackWalkerWalkingThread = value;
+#endif
+ }
+
+ ~StackWalkerWalkingThreadHolder()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+ t_pStackWalkerWalkingThread = m_PreviousValue;
+#endif
+ }
+
+private:
+ Thread* m_PreviousValue;
+};
+
#endif //__threads_h__
#ifndef DACCESS_COMPILE
+#ifdef HOST_WINDOWS
+EXTERN_C UINT32 _tls_index;
+#endif
+
#ifdef _MSC_VER
__declspec(selectany) __declspec(thread) ThreadLocalInfo gCurrentThreadInfo;
#else
config->SetNextInSameThread(nullptr);
}
+#ifdef HOST_WINDOWS
+inline size_t Thread::GetOffsetOfThreadStatic(void* pThreadStatic)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ PTEB Teb = NtCurrentTeb();
+ BYTE** tlsArray = (BYTE**)Teb->ThreadLocalStoragePointer;
+ BYTE* tlsData = (BYTE*)tlsArray[_tls_index];
+
+ return (BYTE*)pThreadStatic - tlsData;
+}
+#endif
+
#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE
#endif
_ASSERTE(CheckPointer(pThread));
#ifndef DISABLE_THREADSUSPEND
- // Only perform this test if we're allowed to call back into the host.
- // Thread::SuspendThread contains several potential calls into the host.
- if (CanThisThreadCallIntoHost())
+ DWORD dwSuspendCount;
+ Thread::SuspendThreadResult str = pThread->SuspendThread(FALSE, &dwSuspendCount);
+ forceStackA = &dwSuspendCount;
+ if (str == Thread::STR_Success)
{
- DWORD dwSuspendCount;
- Thread::SuspendThreadResult str = pThread->SuspendThread(FALSE, &dwSuspendCount);
- forceStackA = &dwSuspendCount;
- if (str == Thread::STR_Success)
- {
- pThread->ResumeThread();
- return dwSuspendCount >= 1;
- }
+ pThread->ResumeThread();
+ return dwSuspendCount >= 1;
}
#endif // !DISABLE_THREADSUSPEND
return TRUE;
// we're doing managed/unmanaged debugging. Calling SetDebugCantStop(true) on the current thread helps us
// remember that.
if (pCurThread)
- pCurThread->SetDebugCantStop(true);
+ IncCantStopCount();
// This is used to avoid thread starvation if non-GC threads are competing for
// the thread store lock when there is a real GC-thread waiting to get in.
// We're out of the critical area for managed/unmanaged debugging.
if (!bThreadDestroyed && pCurThread)
- pCurThread->SetDebugCantStop(false);
+ DecCantStopCount();
}
#ifdef _DEBUG
else
// Mark that we are performing a stackwalker like operation on the current thread.
// This is necessary to allow the signature parsing functions to work without triggering any loads.
- ClrFlsValueSwitch threadStackWalking(TlsIdx_StackWalkerWalkingThread, pThread);
+ StackWalkerWalkingThreadHolder threadStackWalking(pThread);
// Hijack the return address to point to the appropriate routine based on the method's return type.
void *pvHijackAddr = GetHijackAddr(pThread, &codeInfo);
#ifndef DACCESS_COMPILE
+#ifdef CROSSGEN_COMPILE
+void ClrFlsSetThreadType(TlsThreadTypeFlag flag)
+{
+}
+
+void ClrFlsClearThreadType(TlsThreadTypeFlag flag)
+{
+}
+#else // CROSSGEN_COMPILE
+
+thread_local size_t t_ThreadType;
+
+void ClrFlsSetThreadType(TlsThreadTypeFlag flag)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ t_ThreadType |= flag;
+
+ // The historic location of ThreadType slot kept for compatibility with SOS
+ // TODO: Introduce DAC API to make this hack unnecessary
+#if defined(_MSC_VER) && defined(HOST_X86)
+ // Workaround for https://developercommunity.visualstudio.com/content/problem/949233/tls-relative-fixup-overflow-tls-section-is-too-lar.html
+ gCurrentThreadInfo.m_EETlsData = (void**)(((size_t)&t_ThreadType ^ 1) - (4 * TlsIdx_ThreadType + 1));
+#else
+ gCurrentThreadInfo.m_EETlsData = (void**)&t_ThreadType - TlsIdx_ThreadType;
+#endif
+}
+
+void ClrFlsClearThreadType(TlsThreadTypeFlag flag)
+{
+ LIMITED_METHOD_CONTRACT;
+ t_ThreadType &= ~flag;
+}
+#endif // CROSSGEN_COMPILE
+
+thread_local size_t t_CantStopCount;
+
// Helper function that encapsulates the parsing rules.
//
// Called first with *pdstout == NULL to figure out how many args there are
typedef Wrapper<NATIVE_LIBRARY_HANDLE, DoNothing<NATIVE_LIBRARY_HANDLE>, VoidFreeNativeLibrary, NULL> NativeLibraryHandleHolder;
-#ifndef TARGET_UNIX
-
-// A holder for memory blocks allocated by Windows. This holder (and any OS APIs you call
-// that allocate objects on your behalf) should not be used when the CLR is memory-hosted.
-
-FORCEINLINE void VoidFreeWinAllocatedBlock(LPVOID pv)
-{
- LIMITED_METHOD_CONTRACT;
-
-#pragma push_macro("GetProcessHeap")
-#pragma push_macro("HeapFree")
-#undef GetProcessHeap
-#undef HeapFree
- // 0: no special flags
- ::HeapFree(::GetProcessHeap(), 0, pv);
-#pragma pop_macro("HeapFree")
-#pragma pop_macro("GetProcessHeap")
-}
-
-typedef Wrapper<LPVOID, DoNothing<LPVOID>, VoidFreeWinAllocatedBlock, NULL> WinAllocatedBlockHolder;
-
-#endif // !TARGET_UNIX
+extern thread_local size_t t_CantStopCount;
// For debugging, we can track arbitrary Can't-Stop regions.
// In V1.0, this was on the Thread object, but we need to track this for threads w/o a Thread object.
FORCEINLINE void IncCantStopCount()
{
- ClrFlsIncrementValue(TlsIdx_CantStopCount, 1);
+ t_CantStopCount++;
}
FORCEINLINE void DecCantStopCount()
{
- ClrFlsIncrementValue(TlsIdx_CantStopCount, -1);
+ t_CantStopCount--;
}
typedef StateHolder<IncCantStopCount, DecCantStopCount> CantStopHolder;
#ifdef _DEBUG
// For debug-only, this can be used w/ a holder to ensure that we're keeping our CS count balanced.
// We should never use this w/ control flow.
-inline int GetCantStopCount()
+inline size_t GetCantStopCount()
{
- return (int) (size_t) ClrFlsGetValue(TlsIdx_CantStopCount);
+ return t_CantStopCount;
}
// At places where we know we're calling out to native code, we can assert that we're NOT in a CS region.
#endif // !TARGET_UNIX
#endif /* _H_UTIL */
-