/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: ThreadHandles.cpp Date: 2021-6-19 Author: Reece ***/ #include #include "Threads.hpp" #include "ThreadHandles.hpp" #include "OSThread.hpp" namespace Aurora::Threading::Threads { struct BasicThreadId { AuUInt64 threadId; AuUInt64 unixThreadId; }; static AuThreadPrimitives::SpinLock gMutex; static AuHashMap gUnixHandlesToThreads; static AuHashMap gNativeHandlesToThreads; 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 = GetCurrentProcess(); if (!DuplicateHandle(process, thread, process, &ret, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS)) { 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) { AU_LOCK_GUARD(gMutex); #if defined(AURORA_IS_POSIX_DERIVED) AuTryInsert(gUnixHandlesToThreads, AuMakePair(id.unixThreadId, handle)); #endif AuTryInsert(gNativeHandlesToThreads, AuMakePair(id.threadId, handle)); } static void ReleaseThisThread(BasicThreadId id) { AU_LOCK_GUARD(gMutex); #if defined(AURORA_IS_POSIX_DERIVED) AuTryDelete(gUnixHandlesToThreads, id.unixThreadId); #endif AuTryDelete(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) { AU_LOCK_GUARD(gMutex); auto itr = gUnixHandlesToThreads.find(validPid); if (itr == gUnixHandlesToThreads.end()) return {}; return itr->second; } IAuroraThread *GetThreadByOSHandle(AuUInt validPid) { AU_LOCK_GUARD(gMutex); auto itr = gNativeHandlesToThreads.find(validPid); if (itr == gNativeHandlesToThreads.end()) return {}; return itr->second; } AUKN_SYM IAuroraThread *GetThread() { 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()); } }