316 lines
8.7 KiB
C++
316 lines
8.7 KiB
C++
#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<CFusionPerThreadData, offsetof( CFusionPerThreadData, m_Linkage )> CFusionTlsList;
|
|
|
|
static CRITICAL_SECTION s_TlsCriticalSection;
|
|
static LIST_ENTRY * g_FusionTlsList = NULL;
|
|
static __int64 storage_g_FusionTlsList[sizeof(LIST_ENTRY)/sizeof(__int64) + 1];
|
|
|
|
template <typename T>
|
|
T* PlacementNew(T* p)
|
|
{
|
|
(void) (new (reinterpret_cast<PVOID>(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<LIST_ENTRY *>(&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<CFusionPerThreadData*>(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<CFusionPerThreadData*>(::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<CFusionPerThreadData*>(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;
|
|
}
|