[+] New TLSView implementation
[+] AuTLSVariable
This commit is contained in:
parent
37472e508e
commit
bd94b73dde
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
@ -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);
|
||||
};
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user