[+] 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 AuMemoryViewStreamWrite = AuMemory::MemoryViewStreamWrite;
template<typename T, bool bIsStatic = false>
using AuTLSVariable = AuThreads::TLSVariable<T, bIsStatic>;
static bool AuIsThreadRunning()
{
return !AuThreads::GetThread()->Exiting();

View File

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

View File

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

View File

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

View File

@ -82,22 +82,25 @@ namespace Aurora::Threading::Threads
OSThread::OSThread(const ThreadInfo &info) : info_(info)
{
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
this->terminateSignal_ = Primitives::EventShared(true, false, true);
this->terminatedSignalLs_ = AuLoop::NewLSEvent(true, false, true);
this->terminateSignalLs_ = AuLoop::NewLSEvent(true, false, true);
this->exitOnlyOnce_ = Primitives::CriticalSectionUnique();
SysAssert(this->terminated_ ? true : false, "out of memory");
this->terminated_ = AuThreadPrimitives::EventShared(true, false, true);
this->terminateSignal_ = AuThreadPrimitives::EventShared(true, false, true);
SysAssert(this->terminatedSignalLs_ && this->terminateSignalLs_ && this->terminated_ && this->terminateSignal_);
}
OSThread::OSThread() : info_(gDummyThreadInfo)
{
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)
@ -155,7 +158,7 @@ namespace Aurora::Threading::Threads
if (this->terminated_)
{
this->exiting_ = true;
if (WaitFor(this->terminated_.get(), 5000))
if (this->terminated_->LockMS(5'000))
{
return;
}
@ -189,6 +192,7 @@ namespace Aurora::Threading::Threads
AU_LOCK_GUARD(this->tlsLock_);
AU_LOCK_GUARD(this->tlsReferenceThread_->tlsLock_);
SetThreadKey(this->tlsReferenceThread_->tls_);
AuExchange(this->tlsReferenceThread_->tls_, this->tls_);
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
void OSThread::HookReleaseThreadResources()
{
tls_.reset();
AU_LOCK_GUARD(this->tlsLock_);
for (const auto &feature : this->threadFeatures_)
{
feature->Cleanup();
@ -224,7 +227,7 @@ namespace Aurora::Threading::Threads
void OSThread::AddLastHopeTlsHook(const AuSPtr<AuThreads::IThreadFeature> &feature)
{
// TODO: mutex
AU_LOCK_GUARD(this->tlsLock_);
if (!feature)
{
return;
@ -443,12 +446,7 @@ namespace Aurora::Threading::Threads
AuSPtr<TLSView> OSThread::GetTlsView()
{
if (!this->tls_)
{
this->tls_ = AuMakeShared<TLSViewImpl>();
}
return this->tls_;
SysPanic("Deprecated Concept");
}
bool OSThread::ExecuteNewOSContext(AuFunction<void()> task)
@ -468,6 +466,11 @@ namespace Aurora::Threading::Threads
AuThreading::ContextYield();
}
if (!this->tls_)
{
this->tls_ = GetThreadKey();
}
this->_ThreadEP();
}, GetName(), this->info_.stackSize);

View File

@ -74,8 +74,8 @@ namespace Aurora::Threading::Threads
#if defined(AURORA_IS_POSIX_DERIVED)
jmp_buf env;
#endif
Primitives::SpinLock tlsLock_;
AuSPtr<TLSView> tls_;
Primitives::Mutex tlsLock_;
AuUInt32 tls_ {};
OSThread * tlsReferenceThread_ {};
AuString name_;
ThreadInfo info_;
@ -94,7 +94,7 @@ namespace Aurora::Threading::Threads
AuSPtr<AuLoop::ILSEvent> terminatedSignalLs_;
bool bLongJmpOnce {};
Primitives::CriticalSectionUnique_t exitOnlyOnce_;
Primitives::CriticalSection exitOnlyOnce_;
AuList<AuSPtr<IThreadFeature>> threadFeatures_;

View File

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

View File

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

View File

@ -9,20 +9,6 @@
namespace Aurora::Threading::Threads
{
class TLSViewImpl : public TLSView
{
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;
};
AuUInt32 GetThreadKey();
void SetThreadKey(AuUInt32 uThreadKey);
}