From e4c5880ac629267fbe0de1bbac79410f1afa5f3d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 22 Jul 2018 01:28:35 +0200 Subject: [PATCH] [util] Fix Win32 thread helper - Implements move semantics for thread objects properly. This is necessary in order to avoid closing the thread handle multiple times, and to avoid passing invalid pointers to the thread function in some cases. - More closely emulates the behaviour of std::thread. --- src/util/thread.h | 94 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/src/util/thread.h b/src/util/thread.h index 6086e6349..c779895c2 100644 --- a/src/util/thread.h +++ b/src/util/thread.h @@ -6,7 +6,11 @@ #include "./com/com_include.h" +#include "./rc/util_rc.h" +#include "./rc/util_rc_ptr.h" + namespace dxvk { + /** * \brief Thread helper class * @@ -16,42 +20,96 @@ namespace dxvk { * around Windows thread functions so that the rest of code just has to use * dxvk::thread class instead of std::thread. */ - class thread { - + class ThreadFn : public RcObject { + using Proc = std::function; public: - thread() - : m_handle(nullptr) { } - - explicit thread(std::function func) : m_proc(func) { - m_handle = ::CreateThread(nullptr, 0, thread::nativeProc, this, 0, nullptr); - - if (!m_handle) + ThreadFn(Proc&& proc) + : m_proc(std::move(proc)) { + m_handle = ::CreateThread(nullptr, 0, + ThreadFn::threadProc, this, 0, nullptr); + + if (m_handle == nullptr) throw DxvkError("Failed to create thread"); } - ~thread() { - if (m_handle) - ::CloseHandle(m_handle); + ~ThreadFn() { + if (this->joinable()) + std::terminate(); + } + + void detach() { + ::CloseHandle(m_handle); + m_handle = nullptr; } void join() { - ::WaitForSingleObject(m_handle, INFINITE); + if(::WaitForSingleObjectEx(m_handle, INFINITE, FALSE) == WAIT_FAILED) + throw DxvkError("Failed to join thread"); + this->detach(); + } + + bool joinable() const { + return m_handle != nullptr; } private: - std::function m_proc; - HANDLE m_handle; + Proc m_proc; + HANDLE m_handle; - static DWORD WINAPI nativeProc(void *arg) { - auto* proc = reinterpret_cast(arg); - proc->m_proc(); + static DWORD WINAPI threadProc(void *arg) { + Rc info = reinterpret_cast(arg); + info->m_proc(); return 0; } }; + + /** + * \brief RAII thread wrapper + * + * Wrapper for \c ThreadFn that can be used + * as a drop-in replacement for \c std::thread. + */ + class thread { + + public: + + thread() { } + + explicit thread(std::function&& func) + : m_thread(new ThreadFn(std::move(func))) { } + + thread(thread&& other) + : m_thread(std::move(other.m_thread)) { } + + thread& operator = (thread&& other) { + m_thread = std::move(other.m_thread); + return *this; + } + + void detach() { + m_thread->detach(); + } + + void join() { + m_thread->join(); + } + + bool joinable() const { + return m_thread != nullptr + && m_thread->joinable(); + } + + private: + + Rc m_thread; + + }; + + namespace this_thread { inline void yield() { Sleep(0);