diff --git a/include/hx/thread/Scratch.hpp b/include/hx/thread/Scratch.hpp new file mode 100644 index 000000000..946eada32 --- /dev/null +++ b/include/hx/thread/Scratch.hpp @@ -0,0 +1,33 @@ +#pragma once + +#ifndef HXCPP_H +#include +#endif + +HX_DECLARE_CLASS2(hx, thread, Thread) + +namespace hx +{ + namespace thread + { + class Scratch final + { + using ReleaseFunc = void(*)(cpp::marshal::View); + + int* count; + ReleaseFunc release; + + public: + static Scratch alloc(int bytes); + + cpp::marshal::View view; + + Scratch(int* _count, cpp::marshal::View _view, ReleaseFunc _release); + Scratch(const Scratch& other); + + ~Scratch(); + + Scratch& operator=(const Scratch& other); + }; + } +} \ No newline at end of file diff --git a/include/hx/thread/Thread.hpp b/include/hx/thread/Thread.hpp new file mode 100644 index 000000000..b34fcc941 --- /dev/null +++ b/include/hx/thread/Thread.hpp @@ -0,0 +1,37 @@ +#pragma once + +#ifndef HXCPP_H +#include +#endif + +HX_DECLARE_CLASS2(hx, thread, Thread) + +namespace hx +{ + namespace thread + { + struct Thread_obj : public hx::Object + { + using CreateFunction = +#if (HXCPP_API_LEVEL >= 500) + Callable; +#else + Dynamic; +#endif + + static Thread create(CreateFunction); + static Thread current(); + static int id(); + + virtual String getName() = 0; + virtual void setName(const String& name) = 0; + + virtual String toString() override = 0; + + virtual void __Mark(HX_MARK_PARAMS) override = 0; +#ifdef HXCPP_VISIT_ALLOCS + virtual void __Visit(HX_VISIT_PARAMS) override = 0; +#endif + }; + } +} \ No newline at end of file diff --git a/include/hx/thread/ThreadLocal.hpp b/include/hx/thread/ThreadLocal.hpp new file mode 100644 index 000000000..5cf19342d --- /dev/null +++ b/include/hx/thread/ThreadLocal.hpp @@ -0,0 +1,26 @@ +#pragma once + +#ifndef HXCPP_H +#include +#endif + +HX_DECLARE_CLASS2(hx, thread, ThreadLocal) + +namespace hx +{ + namespace thread + { + class ThreadLocal_obj : public hx::Object + { + struct Impl; + + Impl* impl; + + public: + ThreadLocal_obj(); + + Dynamic get(); + void set(Dynamic obj); + }; + } +} \ No newline at end of file diff --git a/src/hx/Debug.cpp b/src/hx/Debug.cpp index 403bf8118..6dc9c0c53 100644 --- a/src/hx/Debug.cpp +++ b/src/hx/Debug.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -244,7 +245,7 @@ StackContext::~StackContext() void StackContext::onThreadAttach() { #ifdef HXCPP_STACK_IDS - mThreadId = __hxcpp_GetCurrentThreadNumber(); + mThreadId = hx::thread::Thread_obj::id(); { std::lock_guard guard(sStackMapMutex); diff --git a/src/hx/Debugger.cpp b/src/hx/Debugger.cpp index 374248272..1dce785dd 100644 --- a/src/hx/Debugger.cpp +++ b/src/hx/Debugger.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include // Newer versions of haxe compiler will set these too (or might be null for haxe 3.0) @@ -1260,7 +1261,7 @@ void __hxcpp_dbg_setEventNotificationHandler(Dynamic handler) if (hx::g_eventNotificationHandler != null()) { GCRemoveRoot(&(hx::g_eventNotificationHandler.mPtr)); } - hx::g_debugThreadNumber = __hxcpp_GetCurrentThreadNumber(); + hx::g_debugThreadNumber = hx::thread::Thread_obj::id(); hx::g_eventNotificationHandler = handler; GCAddRoot(&(hx::g_eventNotificationHandler.mPtr)); } @@ -1275,7 +1276,7 @@ void __hxcpp_dbg_enableCurrentThreadDebugging(bool enable) int __hxcpp_dbg_getCurrentThreadNumber() { - return __hxcpp_GetCurrentThreadNumber(); + return hx::thread::Thread_obj::id(); } diff --git a/src/hx/Thread.cpp b/src/hx/Thread.cpp index f5eae2a36..8e26826ac 100644 --- a/src/hx/Thread.cpp +++ b/src/hx/Thread.cpp @@ -5,28 +5,9 @@ #include #include #include +#include "thread/ThreadImpl.hpp" #include -DECLARE_TLS_DATA(class hxThreadInfo, tlsCurrentThread); - -// Thread number 0 is reserved for the main thread -static std::atomic_int g_nextThreadNumber(1); - - -// How to manage hxThreadInfo references for non haxe threads (main, extenal)? -// HXCPP_THREAD_INFO_PTHREAD - use pthread api -// HXCPP_THREAD_INFO_LOCAL - use thread_local storage -// HXCPP_THREAD_INFO_SINGLETON - use one structure for all threads. Not ideal. - -#if __cplusplus > 199711L && !defined(__BORLANDC__) - #define HXCPP_THREAD_INFO_LOCAL -#elif defined (HXCPP_PTHREADS) - #define HXCPP_THREAD_INFO_PTHREAD -#else - #define HXCPP_THREAD_INFO_SINGLETON -#endif - - // --- Deque ---------------------------------------------------------- struct Deque : public Array_obj @@ -171,265 +152,49 @@ Dynamic __hxcpp_deque_pop(Dynamic q,bool block) return d->PopFront(block); } - - // --- Thread ---------------------------------------------------------- -class hxThreadInfo : public hx::Object -{ -public: - typedef -#if (HXCPP_API_LEVEL>=500) - hx::Callable -#else - Dynamic -#endif - ThreadFuncType; - - HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdThreadInfo }; - - hxThreadInfo(ThreadFuncType inFunction, int inThreadNumber) - : mFunction(inFunction), mThreadNumber(inThreadNumber), mTLS(0,0) - { - mSemaphore = new HxSemaphore; - mDeque = Deque::Create(); - HX_OBJ_WB_NEW_MARKED_OBJECT(this); - } - hxThreadInfo() - { - mSemaphore = 0; - mDeque = Deque::Create(); - HX_OBJ_WB_NEW_MARKED_OBJECT(this); - } - int GetThreadNumber() const - { - return mThreadNumber; - } - void CleanSemaphore() - { - delete mSemaphore; - mSemaphore = 0; - } - void Send(Dynamic inMessage) - { - mDeque->PushBack(inMessage); - } - Dynamic ReadMessage(bool inBlocked) - { - return mDeque->PopFront(inBlocked); - } - String toString() - { - return String(GetThreadNumber()); - } - void SetTLS(int inID,Dynamic inVal) { - mTLS->__SetItem(inID,inVal); - } - Dynamic GetTLS(int inID) { return mTLS[inID]; } - - void __Mark(hx::MarkContext *__inCtx) - { - HX_MARK_MEMBER(mFunction); - HX_MARK_MEMBER(mTLS); - if (mDeque) - HX_MARK_OBJECT(mDeque); - } - #ifdef HXCPP_VISIT_ALLOCS - void __Visit(hx::VisitContext *__inCtx) - { - HX_VISIT_MEMBER(mFunction); - HX_VISIT_MEMBER(mTLS); - if (mDeque) - HX_VISIT_OBJECT(mDeque); - } - #endif - - - Array mTLS; - HxSemaphore *mSemaphore; - ThreadFuncType mFunction; - int mThreadNumber; - Deque *mDeque; -}; - - -THREAD_FUNC_TYPE hxThreadFunc( void *inInfo ) -{ - // info[1] will the the "top of stack" - values under this - // (ie info[0] and other stack values) will be in the GC conservative range - hxThreadInfo *info[2]; - info[0] = (hxThreadInfo *)inInfo; - info[1] = 0; - - tlsCurrentThread = info[0]; - - hx::SetTopOfStack((int *)&info[1], true); - - // Release the creation function - info[0]->mSemaphore->Set(); - - // Call the debugger function to annouce that a thread has been created - //__hxcpp_dbg_threadCreatedOrTerminated(info[0]->GetThreadNumber(), true); - - if ( info[0]->mFunction.GetPtr() ) - { - // Try ... catch - info[0]->mFunction(); - } - - // Call the debugger function to annouce that a thread has terminated - //__hxcpp_dbg_threadCreatedOrTerminated(info[0]->GetThreadNumber(), false); - - hx::UnregisterCurrentThread(); - - tlsCurrentThread = 0; - - THREAD_FUNC_RET -} - - #if (HXCPP_API_LEVEL>=500) Dynamic __hxcpp_thread_create(hx::Callable inStart) #else Dynamic __hxcpp_thread_create(Dynamic inStart) #endif { - #ifdef EMSCRIPTEN - return hx::Throw( HX_CSTRING("Threads are not supported on Emscripten") ); - #else - int threadNumber = g_nextThreadNumber++; - - hxThreadInfo *info = new hxThreadInfo(inStart, threadNumber); - - hx::GCPrepareMultiThreaded(); - hx::EnterGCFreeZone(); - - bool ok = HxCreateDetachedThread(hxThreadFunc, info); - if (ok) - { - info->mSemaphore->Wait(); - } - - hx::ExitGCFreeZone(); - info->CleanSemaphore(); - - if (!ok) - throw Dynamic( HX_CSTRING("Could not create thread") ); - return info; - #endif -} - -#ifdef HXCPP_THREAD_INFO_PTHREAD -static pthread_key_t externThreadInfoKey;; -static pthread_once_t key_once = PTHREAD_ONCE_INIT; -static void destroyThreadInfo(void *i) -{ - hx::Object **threadRoot = (hx::Object **)i; - hx::GCRemoveRoot(threadRoot); - delete threadRoot; -} -static void make_key() -{ - pthread_key_create(&externThreadInfoKey, destroyThreadInfo); -} -#elif defined(HXCPP_THREAD_INFO_LOCAL) -struct ThreadInfoHolder -{ - hx::Object **threadRoot; - ThreadInfoHolder() : threadRoot(0) { } - ~ThreadInfoHolder() - { - if (threadRoot) - { - hx::GCRemoveRoot(threadRoot); - delete threadRoot; - } - } - void set(hx::Object **info) { threadRoot = info; } - hxThreadInfo *get() { return threadRoot ? (hxThreadInfo *)*threadRoot : nullptr; } - -}; -static thread_local ThreadInfoHolder threadHolder; -#else -static hx::Object **sMainThreadInfoRoot = 0; -#endif - -static hxThreadInfo *GetCurrentInfo(bool createNew = true) -{ - hxThreadInfo *info = tlsCurrentThread; - if (!info) - { - #ifdef HXCPP_THREAD_INFO_PTHREAD - pthread_once(&key_once, make_key); - hxThreadInfo **pp = (hxThreadInfo **)pthread_getspecific(externThreadInfoKey); - if (pp) - info = *pp; - #elif defined(HXCPP_THREAD_INFO_LOCAL) - info = threadHolder.get(); - #else - if (sMainThreadInfoRoot) - info = (hxThreadInfo *)*sMainThreadInfoRoot; - #endif - } - - if (!info && createNew) - { - // New, non-haxe thread - might be the first thread, or might be a new - // foreign thread. - info = new hxThreadInfo(null(), 0); - hx::Object **threadRoot = new hx::Object *; - *threadRoot = info; - hx::GCAddRoot(threadRoot); - #ifdef HXCPP_THREAD_INFO_PTHREAD - pthread_setspecific(externThreadInfoKey, threadRoot); - #elif defined(HXCPP_THREAD_INFO_LOCAL) - threadHolder.set(threadRoot); - #else - sMainThreadInfoRoot = threadRoot; - #endif - } - return info; + return hx::thread::Thread_obj::create(inStart); } Dynamic __hxcpp_thread_current() { - return GetCurrentInfo(); + return hx::thread::Thread_obj::current(); } void __hxcpp_thread_send(Dynamic inThread, Dynamic inMessage) { - hxThreadInfo *info = dynamic_cast(inThread.mPtr); - if (!info) - throw HX_INVALID_OBJECT; - info->Send(inMessage); + hx::Throw(HX_CSTRING("Not Implemented")); } Dynamic __hxcpp_thread_read_message(bool inBlocked) { - hxThreadInfo *info = GetCurrentInfo(); - return info->ReadMessage(inBlocked); + return hx::Throw(HX_CSTRING("Not Implemented")); } bool __hxcpp_is_current_thread(hx::Object *inThread) { - hxThreadInfo *info = tlsCurrentThread; - return info==inThread; + return inThread == hx::thread::Thread_obj::current(); } // --- TLS ------------------------------------------------------------ Dynamic __hxcpp_tls_get(int inID) { - return GetCurrentInfo()->GetTLS(inID); + return reinterpret_cast(hx::thread::Thread_obj::current().GetPtr())->getSlot(inID); } void __hxcpp_tls_set(int inID,Dynamic inVal) { - GetCurrentInfo()->SetTLS(inID,inVal); + reinterpret_cast(hx::thread::Thread_obj::current().GetPtr())->setSlot(inID, inVal); } - - // --- Mutex ------------------------------------------------------------ Dynamic __hxcpp_mutex_create() @@ -636,13 +401,7 @@ void __hxcpp_lock_release(Dynamic inlock) int __hxcpp_GetCurrentThreadNumber() { - // Can't allow GetCurrentInfo() to create the main thread's info - // because that can cause a call loop. - hxThreadInfo *threadInfo = GetCurrentInfo(false); - if (!threadInfo) { - return 0; - } - return threadInfo->GetThreadNumber(); + return hx::thread::Thread_obj::id(); } // --- Atomic --- diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 0f7b41c6b..74672a54a 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../Hash.h" #include "GcRegCapture.h" #include @@ -6574,7 +6575,7 @@ void InitAlloc() ExitGCFreeZone(); // Setup main thread ... - __hxcpp_thread_current(); + hx::thread::Thread_obj::current(); gMainThreadContext->onThreadAttach(); } diff --git a/src/hx/thread/Scratch.cpp b/src/hx/thread/Scratch.cpp new file mode 100644 index 000000000..d6ca33cf9 --- /dev/null +++ b/src/hx/thread/Scratch.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "ThreadImpl.hpp" + +hx::thread::Scratch hx::thread::Scratch::alloc(int bytes) +{ + auto current = reinterpret_cast(hx::thread::Thread_obj::current().GetPtr()); + auto required = bytes + sizeof(int); + + // If there is not enough space in the thread array then do a standard malloc. + // Maybe we want some warning log message so the user knows it's hitting a slower path? + if (required > static_cast(current->scratch->length) - current->cursor) + { + auto alloc = std::malloc(required); + auto storage = new(alloc) int; + auto view = cpp::marshal::View(static_cast(alloc) + sizeof(int), bytes); + + std::memset(alloc, 0, required); + + return + hx::thread::Scratch(storage, view, [](cpp::marshal::View view) { + std::free(view.ptr.ptr - sizeof(int)); + }); + } + else + { + auto storage = new(current->scratch->GetBase() + current->cursor) int; + auto view = cpp::marshal::View(current->scratch->GetBase() + current->cursor + sizeof(int), bytes); + + current->cursor += bytes + sizeof(int); + + return + hx::thread::Scratch(storage, view, [](cpp::marshal::View view) { + view.fill(0); + + auto current = reinterpret_cast(hx::thread::Thread_obj::current().GetPtr()); + + current->cursor -= view.length + sizeof(int); + }); + } +} + +hx::thread::Scratch::Scratch(int* _count, cpp::marshal::View _view, ReleaseFunc _release) : count(_count), view(_view), release(_release) +{ + (*count)++; +} + +hx::thread::Scratch::Scratch(const Scratch& _other) : count(_other.count), view(_other.view), release(_other.release) +{ + (*count)++; +} + +hx::thread::Scratch::~Scratch() +{ + if (0 == --(*count)) + { + release(view); + } +} + +hx::thread::Scratch& hx::thread::Scratch::operator=(const Scratch& _other) +{ + auto old = *count; + + count = _other.count; + (*count)++; + + if (0 == (--old)) + { + release(view); + } + + return *this; +} \ No newline at end of file diff --git a/src/hx/thread/ThreadImpl.cpp b/src/hx/thread/ThreadImpl.cpp new file mode 100644 index 000000000..1714eaca6 --- /dev/null +++ b/src/hx/thread/ThreadImpl.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include "ThreadImpl.hpp" + +namespace +{ + struct ThreadInfoHolder + { + hx::Object** root = nullptr; + + ~ThreadInfoHolder() + { + if (root) + { + hx::GCRemoveRoot(root); + + delete root; + } + } + + void set(hx::Object** _root) + { + root = _root; + } + + hx::thread::ThreadImpl_obj* get() const + { + return root ? reinterpret_cast(*root) : nullptr; + } + }; + + thread_local ThreadInfoHolder tls; + + std::atomic_int nextThreadnumber(0); + + void run(hx::thread::ThreadImpl thread, hx::thread::Thread_obj::CreateFunction job, hx::thread::CountingSemaphore semaphore) + { + // info[1] will the the "top of stack" - values under this + // (ie info[0] and other stack values) will be in the GC conservative range + // + // I'm assuming the array of two elements is some trick to ensure it's on the stack. + // Maybe we could use OS specific functions to get the stack range? + + auto root = new hx::Object* { thread.GetPtr() }; + + hx::GCAddRoot(root); + + tls.set(root); + + auto info = std::array{ thread.GetPtr(), nullptr }; + + hx::SetTopOfStack(reinterpret_cast(&info[1]), true); + + // Release the creation function + semaphore->release(); + + job(); + + hx::UnregisterCurrentThread(); + } +} + +hx::thread::Thread hx::thread::Thread_obj::create(CreateFunction job) +{ +#ifdef EMSCRIPTEN + return hx::Throw(HX_CSTRING("Threads are not supported on Emscripten")); +#else + + auto semaphore = new hx::thread::CountingSemaphore_obj(0); + auto obj = new ThreadImpl_obj(nextThreadnumber++, job, semaphore); + + hx::GCSetFinalizer(obj, ThreadImpl_obj::finalise); + hx::GCPrepareMultiThreaded(); + + semaphore->acquire(); + + return hx::thread::Thread{ obj }; +#endif +} + +String hx::thread::ThreadImpl_obj::toString() +{ + return String(id); +} + +hx::thread::Thread hx::thread::Thread_obj::current() +{ + auto info = tls.get(); + if (nullptr == info) + { + // Threads created from Haxe have the TLS set, and the GC sets the TLS for the main thread. + // So, if the TLS is null then we are in a attached foreign thread, which will just get assigned the next Id. + // + // The C++ std has no way of getting the native handle of the current thread, so we need to use OS specific + // apis to get that so we can get and set thread names. + info = new ThreadImpl_obj(nextThreadnumber++); + + auto root = new hx::Object* { info }; + + hx::GCSetFinalizer(info, ThreadImpl_obj::finalise); + hx::GCAddRoot(root); + + tls.set(root); + } + + return Thread{ info }; +} + +int hx::thread::Thread_obj::id() +{ + auto info = tls.get(); + + if (nullptr == info) + { + return 0; + } + else + { + return info->id; + } +} + +hx::thread::ThreadImpl_obj::ThreadImpl_obj(const int _id) + : id(_id) + , native(new Native()) + , scratch(std::numeric_limits::max(), std::numeric_limits::max()) + , slots(0, 0) { } + +hx::thread::ThreadImpl_obj::ThreadImpl_obj(const int _id, Thread_obj::CreateFunction _job, CountingSemaphore _semaphore) + : id(_id) + , native(new Native(_job, this, _semaphore)) + , scratch(std::numeric_limits::max(), std::numeric_limits::max()) + , slots(0, 0) +{ +} + +hx::thread::ThreadImpl_obj::Native::Native(Thread_obj::CreateFunction _job, ThreadImpl _thread, CountingSemaphore _semaphore) + : thread(run, _thread, _job, _semaphore) + , handle(thread.native_handle()) +{ + // Only Windows implements thread name getting and setting currently, and this requires the thread to be attached. + // Threads are normally only detached once the thread object is finalised and this was causing the 32bit linux tests to hit OS resource limits. + // This does feel like a bit of a bodge and something which will actually need to be addressed to add name setting on Linux. + +#ifndef HX_WINDOWS + thread.detach(); +#endif +} + +Dynamic hx::thread::ThreadImpl_obj::getSlot(const int id) +{ + return slots[id]; +} + +void hx::thread::ThreadImpl_obj::setSlot(const int id, const Dynamic& obj) +{ + slots->__SetItem(id, obj); +} + +void hx::thread::ThreadImpl_obj::__Mark(HX_MARK_PARAMS) +{ + HX_MARK_MEMBER(scratch); + HX_MARK_MEMBER(slots); +} + +#ifdef HXCPP_VISIT_ALLOCS + +void hx::thread::ThreadImpl_obj::__Visit(HX_VISIT_PARAMS) +{ + HX_VISIT_MEMBER(scratch); + HX_VISIT_MEMBER(slots); +} + +void hx::thread::ThreadImpl_obj::finalise(hx::Object* obj) +{ + auto thread = reinterpret_cast(obj); + auto native = std::unique_ptr{ thread->native }; + + if (native->thread.joinable()) + { + native->thread.detach(); + } +} + +#endif \ No newline at end of file diff --git a/src/hx/thread/ThreadImpl.hpp b/src/hx/thread/ThreadImpl.hpp new file mode 100644 index 000000000..625bb5d6d --- /dev/null +++ b/src/hx/thread/ThreadImpl.hpp @@ -0,0 +1,58 @@ +#pragma once + +#ifndef HXCPP_H +#include +#endif + +#include +#include +#include +#include + +HX_DECLARE_CLASS2(hx, thread, ThreadImpl) + +namespace hx +{ + namespace thread + { + class ThreadImpl_obj final : public Thread_obj + { + int cursor; + Array scratch; + Array slots; + + public: + friend class Scratch; + + struct Native + { + std::thread thread; + std::thread::native_handle_type handle; + + Native(); + Native(Thread_obj::CreateFunction _job, ThreadImpl _thread, CountingSemaphore _semaphore); + }; + + Native* native; + const int id; + + ThreadImpl_obj(const int _id); + ThreadImpl_obj(const int _id, Thread_obj::CreateFunction _run, CountingSemaphore _semaphore); + + String getName() override; + void setName(const String& name) override; + + Dynamic getSlot(const int id); + void setSlot(const int id, const Dynamic& obj); + + String toString() override; + + void __Mark(HX_MARK_PARAMS) override; +#ifdef HXCPP_VISIT_ALLOCS + void __Visit(HX_VISIT_PARAMS) override; +#endif + + static void finalise(hx::Object* obj); + }; + } +} \ No newline at end of file diff --git a/src/hx/thread/ThreadImpl.noop.cpp b/src/hx/thread/ThreadImpl.noop.cpp new file mode 100644 index 000000000..cdc9c768e --- /dev/null +++ b/src/hx/thread/ThreadImpl.noop.cpp @@ -0,0 +1,15 @@ +#include +#include +#include "ThreadImpl.hpp" + +String hx::thread::ThreadImpl_obj::getName() +{ + return null(); +} + +void hx::thread::ThreadImpl_obj::setName(const String& name) +{ + // +} + +hx::thread::ThreadImpl_obj::Native::Native() : thread(), handle() {} \ No newline at end of file diff --git a/src/hx/thread/ThreadImpl.win32.cpp b/src/hx/thread/ThreadImpl.win32.cpp new file mode 100644 index 000000000..7d8bc45ff --- /dev/null +++ b/src/hx/thread/ThreadImpl.win32.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include "ThreadImpl.hpp" +#include + +String hx::thread::ThreadImpl_obj::getName() +{ + hx::EnterGCFreeZone(); + + auto buffer = PWSTR{ nullptr }; + auto result = GetThreadDescription(native->handle, &buffer); + if (FAILED(result)) + { + _com_error error(result); + + auto msg = error.ErrorMessage(); + + hx::ExitGCFreeZone(); + hx::Throw(String::create(msg)); + } + + hx::ExitGCFreeZone(); + + auto converted = String::create(buffer); + + LocalFree(buffer); + + return converted; +} + +void hx::thread::ThreadImpl_obj::setName(const String& name) +{ + hx::strbuf buffer; + hx::EnterGCFreeZone(); + + auto result = SetThreadDescription(native->handle, name.wchar_str(&buffer)); + if (FAILED(result)) + { + _com_error error(result); + + auto msg = error.ErrorMessage(); + + hx::ExitGCFreeZone(); + hx::Throw(String::create(msg)); + } + else + { + hx::ExitGCFreeZone(); + } +} + +hx::thread::ThreadImpl_obj::Native::Native() : thread(), handle(GetCurrentThread()) {} diff --git a/src/hx/thread/ThreadLocal.cpp b/src/hx/thread/ThreadLocal.cpp new file mode 100644 index 000000000..aabf2800d --- /dev/null +++ b/src/hx/thread/ThreadLocal.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ThreadImpl.hpp" + +struct hx::thread::ThreadLocal_obj::Impl +{ + static std::mutex slotLock; + static std::list slots; + static int counter; + + const int slot; + + Impl(const int _slot) : slot(_slot) {} + + static Impl* get() + { + std::lock_guard lock(slotLock); + + if (slots.empty()) + { + return new Impl(counter++); + } + + auto recycled = slots.front(); + + slots.pop_front(); + + return recycled; + } + + static void finalise(hx::Object* obj) + { + std::lock_guard lock(slotLock); + + slots.emplace_back(reinterpret_cast(obj)->impl); + } +}; + +std::mutex hx::thread::ThreadLocal_obj::Impl::slotLock; + +std::list hx::thread::ThreadLocal_obj::Impl::slots; + +int hx::thread::ThreadLocal_obj::Impl::counter = 0; + +hx::thread::ThreadLocal_obj::ThreadLocal_obj() : impl(Impl::get()) +{ + hx::GCSetFinalizer(this, Impl::finalise); +} + +Dynamic hx::thread::ThreadLocal_obj::get() +{ + auto current = reinterpret_cast(Thread_obj::current().GetPtr()); + + return current->getSlot(impl->slot); +} + +void hx::thread::ThreadLocal_obj::set(Dynamic obj) +{ + auto current = reinterpret_cast(Thread_obj::current().GetPtr()); + + current->setSlot(impl->slot, obj); +} \ No newline at end of file diff --git a/toolchain/haxe-target.xml b/toolchain/haxe-target.xml index d2fd1a1ee..cd9790db5 100644 --- a/toolchain/haxe-target.xml +++ b/toolchain/haxe-target.xml @@ -77,6 +77,10 @@ + + + +
@@ -166,6 +170,9 @@ + + + @@ -208,9 +215,16 @@ + + +
+ +
+
+