/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuThreadHandles.cpp Date: 2021-6-19 Author: Reece ***/ #include #include "AuThreads.hpp" #include "AuThreadHandles.hpp" #include "AuOSThread.hpp" namespace Aurora::Threading::Threads { struct BasicThreadId { AuUInt64 threadId; AuUInt64 unixThreadId; }; struct Temp { ~Temp() { bActive = false; } bool bActive {true}; AuThreadPrimitives::SpinLock gMutex; AuHashMap gUnixHandlesToThreads; AuHashMap gNativeHandlesToThreads; } gVariables; static IAuroraThread *OsThreadAnnounce(AuUInt64 threadId); static void OsThreadShutdown(AuUInt64 threadId); static AuUInt64 GetThreadIdHandle() { #if defined(AURORA_IS_MODERNNT_DERIVED) // Note: The OsThreadAnnounce function takes ownership of win32 handles. // The detor of OSThread is responsible for cleaning up this handle. HANDLE ret; auto process = GetCurrentProcess(); auto thread = GetCurrentThread(); if (!DuplicateHandle(process, thread, process, &ret, THREAD_ALL_ACCESS, FALSE, FALSE)) { return reinterpret_cast(INVALID_HANDLE_VALUE); } return reinterpret_cast(ret); #elif defined(AURORA_HAS_PTHREADS) return pthread_self(); // todo: it's a shame. this is a huge hack, because, all we're doing is providing a pthread type for the OSThread constructor // pthread handles free by magic. we don't need to destroy this. we can get away with chucking this as an handle in later POSIX versions #endif } static void RegisterThisThread(BasicThreadId id, IAuroraThread *handle) { if (!gVariables.bActive) { return; } AU_LOCK_GUARD(gVariables.gMutex); #if defined(AURORA_IS_POSIX_DERIVED) AuTryInsert(gVariables.gUnixHandlesToThreads, id.unixThreadId, handle); #endif AuTryInsert(gVariables.gNativeHandlesToThreads, id.threadId, handle); } static void ReleaseThisThread(BasicThreadId id) { if (!gVariables.bActive) { return; } AU_LOCK_GUARD(gVariables.gMutex); #if defined(AURORA_IS_POSIX_DERIVED) AuTryRemove(gVariables.gUnixHandlesToThreads, id.unixThreadId); #endif AuTryRemove(gVariables.gNativeHandlesToThreads, id.threadId); } struct OSFallbackThread : BasicThreadId { IAuroraThread *handle; OSFallbackThread() { threadId = GetThreadIdHandle(); handle = OsThreadAnnounce(threadId); #if defined(AURORA_IS_POSIX_DERIVED) unixThreadId = gettid(); #endif RegisterThisThread({threadId, unixThreadId}, handle); } ~OSFallbackThread() { OsThreadShutdown(threadId); ReleaseThisThread({threadId, unixThreadId}); } }; #if defined(AURORA_COMPILER_MSVC) || defined(AURORA_COMPILER_CLANG) #define DEFINE_SPEEDY_TLS(type, name) static thread_local type name; #elif defined(AURORA_COMPILER_GCC) #define DEFINE_SPEEDY_TLS(type, name) __thread type name __attribute__((tls_model("initial-exec"))); #else #define DEFINE_SPEEDY_TLS(type, name) static thread_local type name; #endif DEFINE_SPEEDY_TLS(OSFallbackThread, tlsOsFallbackThread); DEFINE_SPEEDY_TLS(IAuroraThread*, tlsCurrentThread); void HandleRemove() { RegisterThisThread({tlsOsFallbackThread.threadId, tlsOsFallbackThread.unixThreadId}, tlsOsFallbackThread.handle); tlsCurrentThread = nullptr; } void HandleRegister(IAuroraThread *thread) { RegisterThisThread({tlsOsFallbackThread.threadId, tlsOsFallbackThread.unixThreadId}, thread); tlsCurrentThread = thread; } IAuroraThread *HandleCurrent() { return tlsCurrentThread; } IAuroraThread *OsThreadAnnounce(AuUInt64 threadId) { SysAssertDbg(tlsOsFallbackThread.threadId == threadId); return _new OSThread(threadId); } void OsThreadShutdown(AuUInt64 threadId) { SysAssertDbg(tlsOsFallbackThread.threadId == threadId); if (tlsOsFallbackThread.handle) { AuSafeDelete(tlsOsFallbackThread.handle); } } IAuroraThread *GetThreadByPOSIXPid(AuUInt validPid) { if (!gVariables.bActive) { return {}; } AU_LOCK_GUARD(gVariables.gMutex); auto itr = gVariables.gUnixHandlesToThreads.find(validPid); if (itr == gVariables.gUnixHandlesToThreads.end()) return {}; return itr->second; } IAuroraThread *GetThreadByOSHandle(AuUInt validPid) { if (!gVariables.bActive) { return {}; } AU_LOCK_GUARD(gVariables.gMutex); auto itr = gVariables.gNativeHandlesToThreads.find(validPid); if (itr == gVariables.gNativeHandlesToThreads.end()) return {}; return itr->second; } AUKN_SYM bool DeadTest() { return !gVariables.bActive; } AUKN_SYM IAuroraThread *GetThread() { if (DeadTest()) { return {}; } auto ret = tlsCurrentThread; if (ret) { return ret; } ret = tlsOsFallbackThread.handle; if (ret) { return ret; } SysPanic("no tls context"); } AUKN_SYM AuThreadId_t GetThreadId() { return reinterpret_cast(GetThread()); } }