windows-nt/Source/XPSP1/NT/base/win32/fusion/utils/fusiontls.cpp
2020-09-26 16:20:57 +08:00

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;
}