[+] New TLSView implementation

[+] AuTLSVariable
This commit is contained in:
Reece Wilson 2023-04-30 07:54:28 +01:00
parent 37472e508e
commit bd94b73dde
10 changed files with 230 additions and 225 deletions

View File

@ -121,6 +121,9 @@ using AuMemoryViewWrite = AuMemory::MemoryViewWrite;
using AuMemoryViewStreamRead = AuMemory::MemoryViewStreamRead; using AuMemoryViewStreamRead = AuMemory::MemoryViewStreamRead;
using AuMemoryViewStreamWrite = AuMemory::MemoryViewStreamWrite; using AuMemoryViewStreamWrite = AuMemory::MemoryViewStreamWrite;
template<typename T, bool bIsStatic = false>
using AuTLSVariable = AuThreads::TLSVariable<T, bIsStatic>;
static bool AuIsThreadRunning() static bool AuIsThreadRunning()
{ {
return !AuThreads::GetThread()->Exiting(); return !AuThreads::GetThread()->Exiting();

View File

@ -19,7 +19,7 @@ namespace Aurora::HWInfo
namespace Aurora::Threading::Threads namespace Aurora::Threading::Threads
{ {
class TLSView; struct TLSView;
/// Threads may be reimplemented as fibers or under some other userland context switcher /// Threads may be reimplemented as fibers or under some other userland context switcher
/// Shared IAuroraThread objects maintain ownership of the execution of the context. /// Shared IAuroraThread objects maintain ownership of the execution of the context.

View File

@ -11,86 +11,91 @@ namespace Aurora::Threading::Threads
{ {
AUKN_SYM bool DeadTest(); AUKN_SYM bool DeadTest();
template<typename T, bool isStatic = false> template<typename T, bool bIsStatic = false>
class TLSVariable struct TLSVariable
{ {
private: TLSVariable()
int _; {
// NOP
public: }
TLSVariable() {}
~TLSVariable() ~TLSVariable()
{ {
if constexpr (!isStatic) #if 0
if constexpr (!bIsStatic)
#endif
{ {
if (DeadTest()) if (DeadTest())
{ {
return; return;
} }
GetThread()->GetTlsView()->Remove(GetHandle());
TLSView::Remove(this->key);
} }
} }
T& operator ->() T *operator ->()
{ {
return Get(); return &Get();
} }
operator T&() const T *operator ->() const
{ {
return Get(); return &Get();
} }
T operator *() operator T &()
{ {
return Get(); return Get();
} }
TLSVariable& operator =(const T & val) operator const T &() const
{
return Get();
}
T operator *() const
{
return Get();
}
TLSVariable &operator =(const T &val)
{ {
Get() = val; Get() = val;
return *this; return *this;
} }
TLSVariable &operator =(T &&val)
{
Get() = AuMove(val);
return *this;
}
private: private:
TLSKey key;
AuUInt64 GetHandle() T &Get() const
{ {
if constexpr (isStatic) return *reinterpret_cast<T *>(TLSView::InitTLS(((TLSVariable *)this)->key, //const-ness isnt enforced here
sizeof(T),
[](void *buffer) -> void
{ {
return (AuUInt64(reinterpret_cast<AuUInt>(&_)) & (~kTlsKeyMask)) | kTlsKeyFollowsConvention | kTlsKeyStaticPointerHandle; if constexpr (AuIsClass_v<T>)
} {
else new (buffer) T();
}
else
{
AuMemset(buffer, 0, sizeof(T));
}
},
[](void *buffer) -> void
{ {
return (AuUInt64(reinterpret_cast<AuUInt>(&_)) & (~kTlsKeyMask)) | kTlsKeyFollowsConvention | kTlsKeyResettablePointerHandle; if constexpr (AuIsClass_v<T>)
}
}
T &Get()
{
auto view = GetThread()->GetTlsView();
auto ptr = view->GetOrSetup(GetHandle(),
sizeof(T),
[](void *buffer) -> void
{ {
if constexpr (AuIsClass_v<T>) reinterpret_cast<T *>(buffer)->~T();
{ }
new (buffer) T(); }));
}
else
{
//std::memset(buffer, 0, sizeof(T));
}
},
[](void *buffer) -> void
{
if constexpr (AuIsClass_v<T>)
{
reinterpret_cast<T *>(buffer)->~T();
}
});
return *reinterpret_cast<T *>(ptr);
} }
}; };
} }

View File

@ -9,34 +9,17 @@
namespace Aurora::Threading::Threads namespace Aurora::Threading::Threads
{ {
static const AuUInt64 kTlsKeyFollowsConvention = AuUInt64(1) << AuUInt64(64 - 1); struct TLSKey
static const AuUInt64 kTlsKeyStaticPointerHandle = AuUInt64(1) << AuUInt64(63 - 1); {
static const AuUInt64 kTlsKeyResettablePointerHandle = AuUInt64(1) << AuUInt64(62 - 1); Primitives::SpinLock lock;
static const AuUInt64 kTlsKeyMask = kTlsKeyResettablePointerHandle | kTlsKeyStaticPointerHandle | kTlsKeyFollowsConvention; void *pContext {};
};
class TLSView
struct AUKN_SYM TLSView
{ {
public:
using TlsCb = AuFunction<void(void *)>; using TlsCb = AuFunction<void(void *)>;
virtual void Remove(AuUInt64 key) = 0; static void Remove(TLSKey &key);
virtual void *GetTLS(AuUInt64 key, AuMach length, bool noAutoCreate = false) = 0; static void *InitTLS(TLSKey &key, AuMach length, const TlsCb &init, const TlsCb &deinit);
virtual void *InitTLS(AuUInt64 key, AuMach length, const TlsCb &init, const TlsCb &deinit) = 0;
void *GetOrSetup(AuUInt64 key, AuMach length, const TlsCb &init, const TlsCb &deinit)
{
void *tls;
#if 0
if (!(tls = GetTLS(key, length, true)))
#endif
{
tls = InitTLS(key, length, init, deinit);
if (!tls)
{
return nullptr;
}
}
return tls;
}
}; };
} }

View File

@ -82,22 +82,25 @@ namespace Aurora::Threading::Threads
OSThread::OSThread(const ThreadInfo &info) : info_(info) OSThread::OSThread(const ThreadInfo &info) : info_(info)
{ {
this->name_ = info.name.value_or("Aurora Thread"); this->name_ = info.name.value_or("Aurora Thread");
this->terminated_ = Primitives::EventShared(true, false, true);
// maybe we should atomic exchange compare these when needed frogthink // maybe we should atomic exchange compare these when needed frogthink
this->terminateSignal_ = Primitives::EventShared(true, false, true);
this->terminatedSignalLs_ = AuLoop::NewLSEvent(true, false, true); this->terminatedSignalLs_ = AuLoop::NewLSEvent(true, false, true);
this->terminateSignalLs_ = AuLoop::NewLSEvent(true, false, true); this->terminateSignalLs_ = AuLoop::NewLSEvent(true, false, true);
this->exitOnlyOnce_ = Primitives::CriticalSectionUnique(); this->terminated_ = AuThreadPrimitives::EventShared(true, false, true);
SysAssert(this->terminated_ ? true : false, "out of memory"); this->terminateSignal_ = AuThreadPrimitives::EventShared(true, false, true);
SysAssert(this->terminatedSignalLs_ && this->terminateSignalLs_ && this->terminated_ && this->terminateSignal_);
} }
OSThread::OSThread() : info_(gDummyThreadInfo) OSThread::OSThread() : info_(gDummyThreadInfo)
{ {
this->name_ = "Main Thread"; this->name_ = "Main Thread";
this->terminated_ = Primitives::EventShared(true, false);
this->exitOnlyOnce_ = Primitives::CriticalSectionUnique(); this->terminated_ = AuThreadPrimitives::EventShared(true, false, true);
this->terminateSignal_ = AuThreadPrimitives::EventShared(true, false, true);
SysAssert(this->terminated_ && this->terminateSignal_);
} }
OSThread::OSThread(AuUInt64 id) : info_(gDummyThreadInfo) OSThread::OSThread(AuUInt64 id) : info_(gDummyThreadInfo)
@ -155,7 +158,7 @@ namespace Aurora::Threading::Threads
if (this->terminated_) if (this->terminated_)
{ {
this->exiting_ = true; this->exiting_ = true;
if (WaitFor(this->terminated_.get(), 5000)) if (this->terminated_->LockMS(5'000))
{ {
return; return;
} }
@ -189,6 +192,7 @@ namespace Aurora::Threading::Threads
AU_LOCK_GUARD(this->tlsLock_); AU_LOCK_GUARD(this->tlsLock_);
AU_LOCK_GUARD(this->tlsReferenceThread_->tlsLock_); AU_LOCK_GUARD(this->tlsReferenceThread_->tlsLock_);
SetThreadKey(this->tlsReferenceThread_->tls_);
AuExchange(this->tlsReferenceThread_->tls_, this->tls_); AuExchange(this->tlsReferenceThread_->tls_, this->tls_);
AuExchange(this->tlsReferenceThread_->threadFeatures_, this->threadFeatures_); AuExchange(this->tlsReferenceThread_->threadFeatures_, this->threadFeatures_);
@ -214,8 +218,7 @@ namespace Aurora::Threading::Threads
// Is is expected for a system thread to release on dtor unlike our aurora threads // Is is expected for a system thread to release on dtor unlike our aurora threads
void OSThread::HookReleaseThreadResources() void OSThread::HookReleaseThreadResources()
{ {
tls_.reset(); AU_LOCK_GUARD(this->tlsLock_);
for (const auto &feature : this->threadFeatures_) for (const auto &feature : this->threadFeatures_)
{ {
feature->Cleanup(); feature->Cleanup();
@ -224,7 +227,7 @@ namespace Aurora::Threading::Threads
void OSThread::AddLastHopeTlsHook(const AuSPtr<AuThreads::IThreadFeature> &feature) void OSThread::AddLastHopeTlsHook(const AuSPtr<AuThreads::IThreadFeature> &feature)
{ {
// TODO: mutex AU_LOCK_GUARD(this->tlsLock_);
if (!feature) if (!feature)
{ {
return; return;
@ -443,12 +446,7 @@ namespace Aurora::Threading::Threads
AuSPtr<TLSView> OSThread::GetTlsView() AuSPtr<TLSView> OSThread::GetTlsView()
{ {
if (!this->tls_) SysPanic("Deprecated Concept");
{
this->tls_ = AuMakeShared<TLSViewImpl>();
}
return this->tls_;
} }
bool OSThread::ExecuteNewOSContext(AuFunction<void()> task) bool OSThread::ExecuteNewOSContext(AuFunction<void()> task)
@ -468,6 +466,11 @@ namespace Aurora::Threading::Threads
AuThreading::ContextYield(); AuThreading::ContextYield();
} }
if (!this->tls_)
{
this->tls_ = GetThreadKey();
}
this->_ThreadEP(); this->_ThreadEP();
}, GetName(), this->info_.stackSize); }, GetName(), this->info_.stackSize);

View File

@ -74,8 +74,8 @@ namespace Aurora::Threading::Threads
#if defined(AURORA_IS_POSIX_DERIVED) #if defined(AURORA_IS_POSIX_DERIVED)
jmp_buf env; jmp_buf env;
#endif #endif
Primitives::SpinLock tlsLock_; Primitives::Mutex tlsLock_;
AuSPtr<TLSView> tls_; AuUInt32 tls_ {};
OSThread * tlsReferenceThread_ {}; OSThread * tlsReferenceThread_ {};
AuString name_; AuString name_;
ThreadInfo info_; ThreadInfo info_;
@ -90,11 +90,11 @@ namespace Aurora::Threading::Threads
bool contextUsed_{}; // can this thread instance execute code again? bool contextUsed_{}; // can this thread instance execute code again?
Primitives::EventShared_t terminated_; Primitives::EventShared_t terminated_;
Primitives::EventShared_t terminateSignal_; Primitives::EventShared_t terminateSignal_;
AuSPtr<AuLoop::ILSEvent> terminateSignalLs_; AuSPtr<AuLoop::ILSEvent> terminateSignalLs_;
AuSPtr<AuLoop::ILSEvent> terminatedSignalLs_; AuSPtr<AuLoop::ILSEvent> terminatedSignalLs_;
bool bLongJmpOnce {}; bool bLongJmpOnce {};
Primitives::CriticalSectionUnique_t exitOnlyOnce_; Primitives::CriticalSection exitOnlyOnce_;
AuList<AuSPtr<IThreadFeature>> threadFeatures_; AuList<AuSPtr<IThreadFeature>> threadFeatures_;

View File

@ -115,7 +115,7 @@ namespace Aurora::Threading::Threads
#endif #endif
DEFINE_SPEEDY_TLS(OSFallbackThread, tlsOsFallbackThread); DEFINE_SPEEDY_TLS(OSFallbackThread, tlsOsFallbackThread);
DEFINE_SPEEDY_TLS(IAuroraThread*, tlsCurrentThread); DEFINE_SPEEDY_TLS(IAuroraThread *, tlsCurrentThread);
void HandleRemove() void HandleRemove()
{ {

View File

@ -13,7 +13,12 @@ namespace Aurora::Threading::Threads
{ {
AUKN_SYM IAuroraThread *ThreadNew(const ThreadInfo &info) AUKN_SYM IAuroraThread *ThreadNew(const ThreadInfo &info)
{ {
if (!info.callbacks) return {}; if (!info.callbacks)
{
SysPushErrorArg();
return {};
}
return _new OSThread(info); return _new OSThread(info);
} }

View File

@ -7,133 +7,153 @@
***/ ***/
#include <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
#include "TLSView.hpp" #include "TLSView.hpp"
#include "../Primitives/ThreadCookie.hpp"
namespace Aurora::Threading::Threads namespace Aurora::Threading::Threads
{ {
void TLSViewImpl::Remove(AuUInt64 key) static AuUInt32 uAtomicCounter {};
static thread_local AuUInt32 tlsThreadIndex { };
static const auto kMaxMaxLinearThreads = AuNumericLimits<AuUInt16>::max() / 2;
AuUInt32 GetThreadKey()
{ {
AU_LOCK_GUARD(lock_); auto &uKey = tlsThreadIndex;
auto a = tls_.find(key); if (!uKey)
if (a == tls_.end())
{ {
return; uKey = AuAtomicAdd(&uAtomicCounter, 1u);
if (uKey > kMaxMaxLinearThreads) [[unlikely]]
{
uKey = uAtomicCounter = kMaxMaxLinearThreads + 1;
}
} }
const auto &handler = a->second.second; return uKey;
if (handler)
{
handler(a->second.first);
}
tls_.erase(a);
} }
TLSViewImpl::~TLSViewImpl()
{
for (const auto &itr : tls_)
{
const auto &handler = itr.second.second;
if (handler)
{
handler(itr.second.first);
}
}
tls_.clear();
}
void *TLSViewImpl::GetTLS(AuUInt64 key, AuMach length, bool noAutoCreate)
{
AU_LOCK_GUARD(lock_);
AuPair<void*, TlsCb>*value;
if (!length)
{
return nullptr;
}
if (!AuTryFind(tls_, key, value))
{
if (noAutoCreate)
{
return nullptr;
}
auto buf = AuMemory::ZAlloc<void *>(length);
if (!buf)
{
return nullptr;
}
auto ok = AuTryInsert(tls_, key, AuMakePair(buf, TlsCb {}));
if (!ok)
{
AuMemory::Free(buf);
return nullptr;
}
ConsiderRehash();
return buf;
}
return value->first;
}
void *TLSViewImpl::InitTLS(AuUInt64 key, AuMach length, const TlsCb &init, const TlsCb &deinit)
{
AU_LOCK_GUARD(lock_);
AuPair<void*, TlsCb>*value;
if (!length)
{
return nullptr;
}
if (AuTryFind(tls_, key, value))
{
return value->first;
}
auto buf = AuMemory::ZAlloc<void *>(length);
if (!buf)
{
return nullptr;
}
TlsCb deinitcb = deinit;
auto ok = AuTryInsert(tls_, key, AuMakePair(buf, deinitcb));
if (!ok)
{
AuMemory::Free(buf);
return nullptr;
}
ConsiderRehash();
if (init) void SetThreadKey(AuUInt32 uThreadKey)
{ {
init(buf); tlsThreadIndex = uThreadKey;
}
return buf;
} }
void TLSViewImpl::ConsiderRehash() struct ViewEntry
{ {
try ~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[32];
AuThreadPrimitives::Mutex mutex;
AuList<AuSPtr<ViewEntry>> ptrs;
AuHashMap<AuUInt, AuSPtr<ViewEntry>> hashMap;
ViewEntry &GetHandleReference();
};
ViewEntry::~ViewEntry()
{
if (this->pVoid && this->deinit)
{ {
// prevent Add, Remove, Add, Remove stalls this->deinit(this->pVoid);
fence_++; }
if ((fence_ % 10) == 0) }
ViewEntry &ViewContext::GetHandleReference()
{
auto uKey = GetThreadKey();
if (uKey >= AuArraySize(this->table))
{
AU_LOCK_GUARD(this->mutex);
if (uKey > kMaxMaxLinearThreads) [[unlikely]]
{ {
tls_.rehash(tls_.size()); auto &sharedRef = this->hashMap[Primitives::GetThreadCookie()];
if (!sharedRef)
{
sharedRef = AuMakeSharedPanic<ViewEntry>();
}
return *sharedRef.get();
}
uKey -= AuArraySize(this->table);
if (uKey >= ptrs.size())
{
ptrs.reserve(uKey * 2);
ptrs.resize(uKey);
}
auto &sharedRef = ptrs[uKey];
if (!sharedRef)
{
sharedRef = AuMakeSharedPanic<ViewEntry>();
}
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();
} }
} }
catch (...) 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;
} }
} }
} }

View File

@ -9,20 +9,6 @@
namespace Aurora::Threading::Threads namespace Aurora::Threading::Threads
{ {
class TLSViewImpl : public TLSView AuUInt32 GetThreadKey();
{ void SetThreadKey(AuUInt32 uThreadKey);
public:
~TLSViewImpl();
void Remove(AuUInt64 key) override;
void *GetTLS(AuUInt64 key, AuMach length, bool noAutoCreate) override;
void *InitTLS(AuUInt64 key, AuMach length, const TlsCb &init, const TlsCb &deinit) override;
private:
void ConsiderRehash();
Primitives::SpinLock lock_;
AuHashMap<AuUInt64, AuPair<void*, TlsCb>> tls_;
AuUInt64 fence_ = 0;
};
} }