/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: TLSView.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "TLSView.hpp" #include "../Primitives/ThreadCookie.hpp" namespace Aurora::Threading::Threads { static AuUInt32 uAtomicCounter {}; static thread_local AuUInt32 tlsThreadIndex { }; static const auto kMaxMaxLinearThreads = AuNumericLimits::max() / 10; AuUInt32 GetThreadKey() { auto &uKey = tlsThreadIndex; if (!uKey) { uKey = AuAtomicAdd(&uAtomicCounter, 1u); if (uKey > kMaxMaxLinearThreads) [[unlikely]] { uKey = uAtomicCounter = kMaxMaxLinearThreads + 1; } } return uKey; } void SetThreadKey(AuUInt32 uThreadKey) { tlsThreadIndex = uThreadKey; } struct ViewEntry { ~ViewEntry(); void *pVoid {}; TLSView::TlsCb deinit; }; struct ViewContext { // 256 bytes, less than some of microsofts stupid stl thread primitives // so, ill take this as acceptable /*void **/ ViewEntry table[64]; AuThreadPrimitives::Mutex mutex; AuList> ptrs; AuHashMap> hashMap; ViewEntry &GetHandleReference(); }; ViewEntry::~ViewEntry() { if (this->pVoid && this->deinit) { this->deinit(this->pVoid); } if (this->pVoid) { AuMemory::_Free(this->pVoid); this->pVoid = nullptr; } } ViewEntry &ViewContext::GetHandleReference() { auto uKey = GetThreadKey(); if (uKey >= AuArraySize(this->table)) { AU_LOCK_GUARD(this->mutex); if (uKey > kMaxMaxLinearThreads) [[unlikely]] { auto &sharedRef = this->hashMap[Primitives::GetThreadCookie()]; if (!sharedRef) { sharedRef = AuMakeSharedPanic(); } return *sharedRef.get(); } uKey -= AuArraySize(this->table); if (uKey >= this->ptrs.size()) { this->ptrs.reserve(uKey * 2); this->ptrs.resize(uKey + 1); } auto &sharedRef = this->ptrs[uKey]; if (!sharedRef) { sharedRef = AuMakeSharedPanic(); } return *sharedRef.get(); } else { return this->table[uKey]; } } void *TLSView::InitTLS(TLSKey &key, AuMach length, const TlsCb &init, const TlsCb &deinit) { ViewContext *pContext; if (!key.pContext) { AU_LOCK_GUARD(key.lock); if (key.pContext) { pContext = (ViewContext *)key.pContext; } else { key.pContext = pContext = new ViewContext(); } } else { pContext = (ViewContext *)key.pContext; } auto &refEntry = pContext->GetHandleReference(); if (refEntry.pVoid) { return refEntry.pVoid; } else { refEntry.pVoid = AuMemory::_FAlloc(length, 64); SysAssert(refEntry.pVoid, "TLS allocation failure"); if (init) { init(refEntry.pVoid); } refEntry.deinit = deinit; return refEntry.pVoid; } } void TLSView::Remove(TLSKey &key) { if (key.pContext) { delete (ViewContext *)key.pContext; } } }