#include "stdinc.h" /*----------------------------------------------------------------------------- Fusion Thread Local Storage (aka Per Thread Data) -----------------------------------------------------------------------------*/ #include "ntrtl.h" #include "fusiontls.h" #include "FusionDequeLinkage.h" #include "FusionDeque.h" #include "FusionEventLog.h" #include "FusionHeap.h" #include "SxsExceptionHandling.h" // // Don't use the FusionTrace functionality in this file. // // #include "fusiontracelight.h" // static DWORD s_dwFusionpThreadLocalIndex = TLS_OUT_OF_INDEXES; // typedef CDeque CFusionTlsList; static CRITICAL_SECTION s_TlsCriticalSection; static LIST_ENTRY * g_FusionTlsList = NULL; static __int64 storage_g_FusionTlsList[sizeof(LIST_ENTRY)/sizeof(__int64) + 1]; template T* PlacementNew(T* p) { (void) (new (reinterpret_cast(p)) T); return p; } BOOL FusionpPerThreadDataMain( HINSTANCE hInst, DWORD dwReason ) { BOOL fResult = FALSE; /* INTERNAL_ERROR_CHECK( ( dwReason != DLL_THREAD_ATTACH ) && ( dwReason != DLL_THREAD_DETACH ) ); */ switch ( dwReason ) { case DLL_PROCESS_ATTACH: __try { if ( !::InitializeCriticalSectionAndSpinCount( &s_TlsCriticalSection, INITIALIZE_CRITICAL_SECTION_AND_SPIN_COUNT_ALLOCATE_NOW ) ) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS: %s - Failed creating TLS critical section, LastError=%08x\n", __FUNCTION__, ::FusionpGetLastWin32Error()); goto Exit; } } __except( EXCEPTION_EXECUTE_HANDLER ) { // // The exception code is interesting. However, we don't have access // (directly) to SxspSetLastNTError yet - this is a hotfix. // // REVIEW: Move SxspSetLastNTError into one of the fusion\utils\*.cpp files // because it seems like a relatively good idea. // ::SetLastError(::FusionpNtStatusToDosError(GetExceptionCode())); goto Exit; } s_dwFusionpThreadLocalIndex = TlsAlloc(); if ( s_dwFusionpThreadLocalIndex == TLS_OUT_OF_INDEXES ) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: %s(): TlsAlloc failed: last error %d\n", __FUNCTION__, FusionpGetLastWin32Error()); goto Exit; } g_FusionTlsList = PlacementNew(reinterpret_cast(&storage_g_FusionTlsList)); if ( g_FusionTlsList == NULL ) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: %s(): Really bad things: placement new of CFusionTlsList failed, 0x%08x\n", __FUNCTION__, ::FusionpGetLastWin32Error()); ::SetLastError(ERROR_INTERNAL_ERROR); goto Exit; } InitializeListHead(g_FusionTlsList); break; case DLL_PROCESS_DETACH: if ( s_dwFusionpThreadLocalIndex != TLS_OUT_OF_INDEXES ) { ::EnterCriticalSection( &s_TlsCriticalSection ); __try { LIST_ENTRY *ple = g_FusionTlsList->Flink; while (ple != g_FusionTlsList) { CFusionPerThreadData *pptd = CONTAINING_RECORD(ple, CFusionPerThreadData, m_Linkage); ple = ple->Flink; FUSION_DELETE_SINGLETON(pptd); } } __finally { ::LeaveCriticalSection( &s_TlsCriticalSection ); } ::TlsFree( s_dwFusionpThreadLocalIndex ); s_dwFusionpThreadLocalIndex = TLS_OUT_OF_INDEXES; } // // Re-entrance intelligence // if (g_FusionTlsList != NULL) { InitializeListHead(g_FusionTlsList); g_FusionTlsList = NULL; } ::DeleteCriticalSection( &s_TlsCriticalSection ); break; } fResult = TRUE; Exit: return fResult; } VOID FusionpClearPerThreadData( VOID ) { CFusionPerThreadData *pPerThread; PVOID pvPerThread; ASSERT_NTC(s_dwFusionpThreadLocalIndex != TLS_OUT_OF_INDEXES); // // Acquire, then clear, this thread's per-thread data // pvPerThread = ::TlsGetValue(s_dwFusionpThreadLocalIndex); // // If the TlsSetValue failed with STATUS_NO_MEMORY, that's just dandy. // Otherwise, something wierd has happened along the line, and maybe // people care to know. // if ( !::TlsSetValue( s_dwFusionpThreadLocalIndex, NULL ) ) { SOFT_ASSERT_NTC(::FusionpGetLastWin32Error() != ERROR_NOT_ENOUGH_MEMORY); } // // If we got something interesting back from TlsGetValue, then cast it // to the Right Thing and delete it. // pPerThread = reinterpret_cast(pvPerThread); if (pPerThread != NULL) FUSION_DELETE_SINGLETON(pPerThread); } CFusionPerThreadData* FusionpGetPerThreadData( EFusionpTls e ) { const DWORD dwLastError = ::FusionpGetLastWin32Error(); CFusionPerThreadData* pTls = NULL; __try { // the use of "temp" here mimics what you would do with a destructor; // have a temp that is unconditionally freed, unless it is nulled by commiting it // into the return value "return pt.Detach();" CFusionPerThreadData* pTlsTemp = reinterpret_cast(::TlsGetValue(s_dwFusionpThreadLocalIndex)); if ((pTlsTemp == NULL) && ((e & eFusionpTlsCreate) != 0)) { if (::FusionpGetLastWin32Error() != NO_ERROR) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: %s() called TlsGetValue() which failed; win32 error = %d\n", __FUNCTION__, ::FusionpGetLastWin32Error()); FUSION_DEBUG_BREAK(); return NULL; } pTlsTemp = reinterpret_cast(FUSION_RAW_ALLOC(sizeof(*pTlsTemp), 'tsxs')); if (pTlsTemp == NULL) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: TLS allocation failed in %s()\n", __FUNCTION__); return NULL; } if (!::TlsSetValue(s_dwFusionpThreadLocalIndex, pTlsTemp)) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: TlsSetValue failed in %s(), lastError: %d\n", __FUNCTION__, FusionpGetLastWin32Error()); FUSION_RAW_DEALLOC(pTlsTemp); return NULL; } ::EnterCriticalSection( &s_TlsCriticalSection ); __try { InsertTailList(g_FusionTlsList, &pTlsTemp->m_Linkage); } __finally { ::LeaveCriticalSection( &s_TlsCriticalSection ); } } pTls = pTlsTemp; pTlsTemp = NULL; } __finally { ::SetLastError(dwLastError); } return pTls; } CFusionPerThreadData * FusionpSetPerThreadData( CFusionPerThreadData *pNewTls, EFusionSetTls Action ) { CSxsPreserveLastError ple; CFusionPerThreadData *pExisting = NULL; if (s_dwFusionpThreadLocalIndex == TLS_OUT_OF_INDEXES) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: Attempt to set per thread data when TLS index was not allocated."); FUSION_DEBUG_BREAK(); ple.Restore(); return NULL; } if ( ( Action != eFusionpTlsSetReplaceExisting ) && ( Action != eFusionpTlsSetIfNull ) && ( Action != 0 ) ) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: Invalid action parameter passed to %s\n", __FUNCTION__); FUSION_DEBUG_BREAK(); ple.Restore(); return NULL; } else if (Action == 0) { Action = eFusionpTlsSetIfNull; } pExisting = ::FusionpGetPerThreadData(); // // If the existing one is null, and we're supposed to set it // if it's null, then set it. Or, if we're suppose to set it // anyhow, then that's fine. // if ((Action == eFusionpTlsSetReplaceExisting) || ((Action == eFusionpTlsSetIfNull) && (pExisting == NULL))) { if ( !::TlsSetValue(s_dwFusionpThreadLocalIndex, pNewTls)) { ple.Restore(); return NULL; } pExisting = pNewTls; } ple.Restore(); return pExisting; }