bool CTLSStorageArray::FindAbandonedStorageBlockIndex(unsigned int &nOutFreeBlockIndex, tlsindextype iValueCount) { bool bResult = false; do { CClientHandleArray haTranslatedHandlesStorage; CHandleTranslationMap tmTranslationMapStorage; const HANDLE *ph_TranslatedHandles; const unsigned int *puiTranslationMap; // Translate handles into array for the case if there are invalids unsigned int nHandleCount = TranslateClientHandles(haTranslatedHandlesStorage, tmTranslationMapStorage, ph_TranslatedHandles, puiTranslationMap); OU_ASSERT(OU_IN_INT_RANGE(nHandleCount, 0, MAXIMUM_WAIT_OBJECTS + 1)); if (nHandleCount == 0) { break; } // Since allocating a new storage block is a relatively slow operation // it is acceptable to enter kernel for checking for exited threads. DWORD dwWaitResult = ::WaitForMultipleObjects(nHandleCount, ph_TranslatedHandles, FALSE, 0); if (!OU_IN_INT_RANGE(dwWaitResult - WAIT_OBJECT_0, 0, nHandleCount)) { // Wait should not normally fail. If it does it's in most cases an indication // of invalid handle passed as parameter. However it may fail because of other // reasons as well. If this assertion fails too often and you are sure all the // handles are valid, it is safe to comment it. OU_ASSERT(dwWaitResult != WAIT_FAILED); break; } unsigned int nTranslatedBlockIndex = (unsigned int)(dwWaitResult - WAIT_OBJECT_0); unsigned int nBlockIndex = !puiTranslationMap ? nTranslatedBlockIndex : puiTranslationMap[nTranslatedBlockIndex]; CTLSStorageBlock *psbStorageBlock = GetStorageBlockPointer(nBlockIndex, iValueCount); ReinitializeStorageSingleBlock(psbStorageBlock, iValueCount); // Close old handle and make a duplicate of current thread handle FreeStorageThreadHandle(nBlockIndex); AllocateBlockThreadHandle(nBlockIndex); nOutFreeBlockIndex = nBlockIndex; bResult = true; } while (false); return bResult; }
unsigned int CTLSStorageArray::GetStorageBlockIndex(CTLSStorageBlock *psbStorageBlock, tlsindextype iValueCount) const { const size_t nHeaderSize = CTLSStorageArray::GetHeaderSize(); const size_t nBlockSize = CTLSStorageBlock::GetRequiredSize(iValueCount); const size_t nBlockZeroOffset = CTLSStorageBlock::GetZeroOffset(iValueCount); unsigned int uiBlockIndex = (unsigned int)((((int8ou *)psbStorageBlock) - nBlockZeroOffset - nHeaderSize - ((int8ou *)this)) / nBlockSize); OU_ASSERT((((int8ou *)psbStorageBlock) - nBlockZeroOffset - nHeaderSize - ((int8ou *)this)) % nBlockSize == 0); OU_ASSERT(OU_IN_INT_RANGE(uiBlockIndex, 0, TLS_ARRAY_ELEMENT__MAX)); return uiBlockIndex; }
bool CTLSInitialization::InitializeTLSAPI(HTLSKEY &hskOutStorageKey, tlsindextype iValueCount, unsigned int uiInitializationFlags/*=0*/) { OU_ASSERT(g_uiThreadLocalStorageInitializationCount != 0U - 1U); bool bResult = false; bool bAtomicAPIInitialized = false; do { const ESTORAGEINSTANCEKIND ikInstanceKind = (uiInitializationFlags & SIF_MANUAL_CLEANUP_ON_THREAD_EXIT) ? SIK_MANUALCLEANUP : SIK_AUTOCLEANUP; if (g_apsiStorageGlobalInstances[ikInstanceKind] == NULL) // Initialization/finalization must be called from main thread { if (!InitializeAtomicAPI()) { break; } bAtomicAPIInitialized = true; if (!InitializeTLSAPIValidated(ikInstanceKind, iValueCount, uiInitializationFlags)) { break; } const HTLSKEYVALUE &hkvStorageKey = g_apsiStorageGlobalInstances[ikInstanceKind]->RetrieveStorageKey(); g_ahkvStorageGlobalKeyValues[ikInstanceKind] = hkvStorageKey; } ++g_uiThreadLocalStorageInitializationCount; hskOutStorageKey = EncodeKeySelectorFromStorageKind(ikInstanceKind); OU_ASSERT(iValueCount == g_apsiStorageGlobalInstances[ikInstanceKind]->RetrieveValueCount()); OU_ASSERT(uiInitializationFlags == g_apsiStorageGlobalInstances[ikInstanceKind]->RetrieveInitializationFlags()); bResult = true; } while (false); if (!bResult) { if (bAtomicAPIInitialized) { FinalizeAtomicAPI(); } } return bResult; }
void CTLSInitialization::FinalizeTLSAPIValidated(unsigned int uiInstanceKind) { OU_ASSERT(g_apsiStorageGlobalInstances[uiInstanceKind] != NULL); g_apsiStorageGlobalInstances[uiInstanceKind]->FreeInstance(); g_apsiStorageGlobalInstances[uiInstanceKind] = NULL; }
void CTLSStorageArray::FreeStorageBlockOnThreadExit(CTLSStorageBlock *psbStorageBlock, tlsindextype iValueCount) { ReinitializeStorageSingleBlock(psbStorageBlock, iValueCount); // OU_ASSERT(GetBlockThreadHandle(nBlockIndex) == INVALID_HANDLE_VALUE) -- assertion further in the code unsigned int nBlockIndex = GetStorageBlockIndex(psbStorageBlock, iValueCount); OU_ASSERT(GetBlockOccupiedFlag(nBlockIndex)); #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS OU_ASSERT(GetBlockThreadHandle(nBlockIndex) == INVALID_HANDLE_VALUE); // The method is not to be called if automatic cleanup is enabled #endif // #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS ResetBlockOccupiedFlag(nBlockIndex); }
bool CThreadLocalStorage::AllocateAndSetStorageValue(const HTLSKEYSELECTOR &hksKeySelector, tlsindextype iValueIndex, tlsvaluetype vValueData, CTLSValueDestructor fnValueDestructor) { OU_ASSERT(OU_IN_SIZET_RANGE(DecodeInstanceKindFromKeySelector(hksKeySelector), SIK__MIN, SIK__MAX)); bool bResult = false; do { ESTORAGEINSTANCEKIND ikInstanceKind = (ESTORAGEINSTANCEKIND)DecodeInstanceKindFromKeySelector(hksKeySelector); CTLSStorageInstance *psiStorageInstance = g_apsiStorageGlobalInstances[ikInstanceKind]; CTLSStorageBlock *psbStorageBlock; if (!psiStorageInstance->FindFreeStorageBlock(psbStorageBlock)) { break; } SetKeyStorageBlock(hksKeySelector, psbStorageBlock); psbStorageBlock->SetValueData(iValueIndex, vValueData); psbStorageBlock->SetValueDestructor(iValueIndex, fnValueDestructor); bResult = true; } while (false); return bResult; }
void CTLSStorageArray::FreeStorageAllBlocks(tlsindextype iValueCount) { for (unsigned int nBlockIndex = 0; nBlockIndex != TLS_ARRAY_ELEMENT__MAX; ++nBlockIndex) { if (GetBlockOccupiedFlag(nBlockIndex)) { CTLSStorageBlock *psbStorageBlock = GetStorageBlockPointer(nBlockIndex, iValueCount); FinalizeStorageSingleBlock(psbStorageBlock, iValueCount); #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS FreeStorageThreadHandle(nBlockIndex); #endif // #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS } else { #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS OU_ASSERT(GetBlockThreadHandle(nBlockIndex) == INVALID_HANDLE_VALUE); // Where did the handle come from if block is not occupied? #endif // #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS } } }
void CTLSInitialization::FinalizeTLSAPIValidated() { OU_ASSERT(g_psiStorageGlobalInstance != NULL); g_psiStorageGlobalInstance->FreeInstance(); g_psiStorageGlobalInstance = NULL; }
bool CTLSStorageInstance::FindFreeStorageBlockInArrayListSegment(CTLSStorageBlock *&psbOutStorageBlock, CTLSStorageArray *psaListSegmentBegin, CTLSStorageArray *psaListSegmentEnd) { OU_ASSERT(psaListSegmentBegin != psaListSegmentEnd); bool bResult; CTLSStorageArray *psaListSegmentCurrent = psaListSegmentBegin; while (true) { if (FindFreeStorageBlockFromArray(psbOutStorageBlock, psaListSegmentCurrent)) { bResult = true; break; } psaListSegmentCurrent = psaListSegmentCurrent->GetNextArray(); if (psaListSegmentCurrent == psaListSegmentEnd) { bResult = false; break; } } return bResult; }
bool CTLSInitialization::InitializeTLSAPI(HTLSKEY &hskOutStorageKey, tlsindextype iValueCount, unsigned int uiInitializationFlags/*=0*/) { OU_ASSERT(g_uiThreadLocalStorageInitializationCount != 0U - 1U); bool bResult = false; bool bAtomicAPIInitialized = false; do { if (g_uiThreadLocalStorageInitializationCount == 0U) // Initialization/finalization must be called from main thread { if (!InitializeAtomicAPI()) { break; } bAtomicAPIInitialized = true; if (!InitializeTLSAPIValidated(iValueCount, uiInitializationFlags)) { break; } } ++g_uiThreadLocalStorageInitializationCount; hskOutStorageKey = g_psiStorageGlobalInstance->RetrieveStorageKey(); OU_ASSERT(iValueCount == g_psiStorageGlobalInstance->RetrieveValueCount()); OU_ASSERT(uiInitializationFlags == g_psiStorageGlobalInstance->RetrieveInitializationFlags()); bResult = true; } while (false); if (!bResult) { if (bAtomicAPIInitialized) { FinalizeAtomicAPI(); } } return bResult; }
/*extern*/ void FinalizeAtomicAPI() { OU_ASSERT(g_uiAtomicAPIInitializationCount != 0U); if (--g_uiAtomicAPIInitializationCount == 0) // Initialization/finalization must be called from main thread { FinalizeAtomicAPIValidated(); } }
CTLSStorageArray::~CTLSStorageArray() { #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS OU_ASSERT(CheckIfAllBlocksHaveInvalidThreads()); #endif // #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS }
CTLSStorageBlock *CTLSStorageArray::GetStorageBlockPointer(unsigned int nBlockIndex, tlsindextype iValueCount) const { OU_ASSERT(OU_IN_INT_RANGE(nBlockIndex, 0, TLS_ARRAY_ELEMENT__MAX)); const size_t nHeaderSize = CTLSStorageArray::GetHeaderSize(); const size_t nBlockSize = CTLSStorageBlock::GetRequiredSize(iValueCount); const size_t nBlockZeroOffset = CTLSStorageBlock::GetZeroOffset(iValueCount); CTLSStorageBlock *psbStorageBlock = (CTLSStorageBlock *)(((int8ou *)this) + nHeaderSize + nBlockIndex * nBlockSize + nBlockZeroOffset); return psbStorageBlock; }
void CTLSInitialization::FinalizeTLSAPI() { OU_ASSERT(g_uiThreadLocalStorageInitializationCount != 0U); if (--g_uiThreadLocalStorageInitializationCount == 0U) // Initialization/finalization must be called from main thread { FinalizeTLSAPIValidated(); FinalizeAtomicAPI(); } }
void CTLSInitialization::CleanupOnThreadExit() { const ESTORAGEINSTANCEKIND ikInstanceKind = SIK_MANUALCLEANUP; CTLSStorageInstance *psiStorageInstance = g_apsiStorageGlobalInstances[ikInstanceKind]; if (psiStorageInstance != NULL) { OU_ASSERT(psiStorageInstance->GetIsThreadManualCleanup()); const HTLSKEYSELECTOR &hksKeySelector = EncodeKeySelectorFromStorageKind(ikInstanceKind); CTLSStorageBlock *psbStorageBlock = CThreadLocalStorage::GetKeyStorageBlock(hksKeySelector); if (psbStorageBlock) { psiStorageInstance->FreeStorageBlockOnThreadExit(psbStorageBlock); CThreadLocalStorage::SetKeyStorageBlock(hksKeySelector, NULL); } } else { OU_ASSERT(false); // The method is not supposed to be called if manual cleanup was not requested on initialization } }
void CTLSInitialization::FinalizeTLSAPI() { OU_ASSERT(g_uiThreadLocalStorageInitializationCount != 0U); ESTORAGEINSTANCEKIND ikInstanceKind = (--g_uiThreadLocalStorageInitializationCount == 0U) ? SIK__MIN : SIK__MAX; // Initialization/finalization must be called from main thread for (; ikInstanceKind != SIK__MAX; ++ikInstanceKind) { if (g_apsiStorageGlobalInstances[ikInstanceKind]) { g_ahkvStorageGlobalKeyValues[ikInstanceKind] = 0; FinalizeTLSAPIValidated(ikInstanceKind); FinalizeAtomicAPI(); } } }
bool CTLSInitialization::InitializeTLSAPIValidated(unsigned int uiInstanceKind, tlsindextype iValueCount, unsigned int uiInitializationFlags) { OU_ASSERT(g_apsiStorageGlobalInstances[uiInstanceKind] == NULL); bool bResult = false; CTLSStorageInstance *psiStorageInstance; do { // Use static methods instead of constructor/destructor // to avoid overloading operators new/delete and for // uniformity with CTLSStorageArray class psiStorageInstance = CTLSStorageInstance::AllocateInstance(iValueCount, uiInitializationFlags); if (!psiStorageInstance) { break; } if (!psiStorageInstance->Init((ESTORAGEINSTANCEKIND)uiInstanceKind)) { break; } g_apsiStorageGlobalInstances[uiInstanceKind] = psiStorageInstance; bResult = true; } while (false); if (!bResult) { if (psiStorageInstance) { psiStorageInstance->FreeInstance(); } } return bResult; }
void CTLSStorageArray::AllocateBlockThreadHandle(unsigned int nBlockIndex) { OU_ASSERT(GetBlockThreadHandle(nBlockIndex) == INVALID_HANDLE_VALUE); HANDLE hCurrentThreadDuplicate; HANDLE hCurrentProcess = ::GetCurrentProcess(); HANDLE hCurrentThread = ::GetCurrentThread(); if (!::DuplicateHandle(hCurrentProcess, hCurrentThread, hCurrentProcess, &hCurrentThreadDuplicate, SYNCHRONIZE, FALSE, 0)) { // Handle duplication should not normally fail. // Thread and process pseudo-handles have full access allowed. // The duplication may only fail in case of kernel internal problems // (like lack of the resources or resource limit hits). // Well, in this case thread data will remain in memory until // CTLSInitialization::FinalizeTLSAPI() is called. hCurrentThreadDuplicate = INVALID_HANDLE_VALUE; } SetBlockThreadHandle(nBlockIndex, hCurrentThreadDuplicate); }
void CTLSInitialization::CleanupOnThreadExit() { CTLSStorageInstance *psiStorageInstance = g_psiStorageGlobalInstance; if (psiStorageInstance->GetIsThreadManualCleanup()) { const HTLSKEY &hskStorageKey = g_psiStorageGlobalInstance->RetrieveStorageKey(); CTLSStorageBlock *psbStorageBlock = CThreadLocalStorage::GetKeyStorageBlock(hskStorageKey); if (psbStorageBlock) { psiStorageInstance->FreeStorageBlockOnThreadExit(psbStorageBlock); CThreadLocalStorage::SetKeyStorageBlock(hskStorageKey, NULL); } } else { OU_ASSERT(false); // The method is not supposed to be called if manual cleanup was not requested on initialization } }
void CTLSStorageInstance::FreeStorageKey(const HTLSKEYVALUE &hkvStorageKey) { #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS DWORD dwTlsIndex = (DWORD)(size_t)(HTLSKEYVALUE::value_type)hkvStorageKey; OU_ASSERT(dwTlsIndex != TLS_OUT_OF_INDEXES); BOOL bIndexFreeingResult = ::TlsFree(dwTlsIndex); OU_VERIFY(bIndexFreeingResult); #else // #if _OU_TARGET_OS != _OU_TARGET_OS_WINDOWS pthread_key_t pkThreadKey = (pthread_key_t)(size_t)(HTLSKEYVALUE::value_type)hkvStorageKey; int iKeyDeletionResult = pthread_key_delete(pkThreadKey); OU_VERIFY(iKeyDeletionResult == EOK); #endif // #if _OU_TARGET_OS == ... }
bool CTLSStorageArray::FindFreeStorageBlockIndex(unsigned int &nOutFreeBlockIndex, tlsindextype /*iValueCount*/, bool bIsManualCleanup) #endif { bool bResult = false; if (!GetAreAllBlocksOccupied() && FindFreeStorageBlockIndexWithPossibilityVerified(nOutFreeBlockIndex, bIsManualCleanup)) { bResult = true; } #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS else if (!bIsManualCleanup) { // Execution gets here is all slots were already occupied or // they become occupied during search (otherwise why // FindFreeStorageBlockIndexWithPossibilityVerified call failed???). // In Automatic cleanup mode a block can't become free by itself - // it is just re-allocated for new thread and remains busy. OU_ASSERT(GetAreAllBlocksOccupied()); // The locking is performed to avoid more than one threads checking // for abandoned handles simultaneously. // If locking fails, execution just proceeds to next array in the chain if (SetArrayLockedFlag()) { bResult = FindAbandonedStorageBlockIndex(nOutFreeBlockIndex, iValueCount); ResetArrayLockedFlag(); } } #endif // #if _OU_TARGET_OS == _OU_TARGET_OS_WINDOWS return bResult; }
/*extern*/ bool InitializeAtomicAPI() { OU_ASSERT(g_uiAtomicAPIInitializationCount != 0U - 1U); bool bResult = false; do { if (g_uiAtomicAPIInitializationCount == 0) // Initialization/finalization must be called from main thread { if (!InitializeAtomicAPIValidated()) { break; } } ++g_uiAtomicAPIInitializationCount; bResult = true; } while (false); return bResult; }