diff options
author | Philip Rebohle <philip.rebohle@tu-dortmund.de> | 2023-04-25 12:49:17 +0300 |
---|---|---|
committer | Philip Rebohle <philip.rebohle@tu-dortmund.de> | 2023-04-25 12:53:58 +0300 |
commit | 5a1980da617d51987821c82750cb2ea96d8067c8 (patch) | |
tree | 78337487c3b7360d672025d9df1471ca6cf245ca | |
parent | e074d83d0b502961604ea00c2ad24508bf33a1a2 (diff) |
[dxvk] Rewrite thread wrapperthread-rework
Addresses some issues raised in #3378.
-rw-r--r-- | src/util/thread.cpp | 76 | ||||
-rw-r--r-- | src/util/thread.h | 139 |
2 files changed, 129 insertions, 86 deletions
diff --git a/src/util/thread.cpp b/src/util/thread.cpp index 044ae696..5c912412 100644 --- a/src/util/thread.cpp +++ b/src/util/thread.cpp @@ -1,10 +1,82 @@ +#include <atomic> + #include "thread.h" #include "util_likely.h" -#include <atomic> - #ifdef _WIN32 +namespace dxvk { + + thread::thread(ThreadProc&& proc) + : m_data(new ThreadData(std::move(proc))) { + m_data->handle = ::CreateThread(nullptr, 0x100000, + thread::threadProc, m_data, STACK_SIZE_PARAM_IS_A_RESERVATION, + &m_data->id); + + if (!m_data->handle) { + delete m_data; + throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again), "Failed to create thread"); + } + } + + + thread::~thread() { + if (joinable()) + std::terminate(); + } + + + void thread::join() { + if (!joinable()) + throw std::system_error(std::make_error_code(std::errc::invalid_argument), "Thread not joinable"); + + if (get_id() == this_thread::get_id()) + throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur), "Cannot join current thread"); + + if(::WaitForSingleObjectEx(m_data->handle, INFINITE, FALSE) == WAIT_FAILED) + throw std::system_error(std::make_error_code(std::errc::invalid_argument), "Joining thread failed"); + + detach(); + } + + + void thread::set_priority(ThreadPriority priority) { + int32_t value; + switch (priority) { + default: + case ThreadPriority::Normal: value = THREAD_PRIORITY_NORMAL; break; + case ThreadPriority::Lowest: value = THREAD_PRIORITY_LOWEST; break; + } + + if (m_data) + ::SetThreadPriority(m_data->handle, int32_t(value)); + } + + + uint32_t thread::hardware_concurrency() { + SYSTEM_INFO info = { }; + ::GetSystemInfo(&info); + return info.dwNumberOfProcessors; + } + + + DWORD WINAPI thread::threadProc(void* arg) { + auto data = reinterpret_cast<ThreadData*>(arg); + DWORD exitCode = 0; + + try { + data->proc(); + } catch (...) { + exitCode = 1; + } + + data->decRef(); + return exitCode; + } + +} + + namespace dxvk::this_thread { bool isInModuleDetachment() { diff --git a/src/util/thread.h b/src/util/thread.h index a0348e68..6e25f407 100644 --- a/src/util/thread.h +++ b/src/util/thread.h @@ -5,6 +5,7 @@ #include <functional> #include <mutex> #include <thread> +#include <utility> #include "util_error.h" @@ -24,126 +25,96 @@ namespace dxvk { }; #ifdef _WIN32 - /** - * \brief Thread helper class - * - * This is needed mostly for winelib builds. Wine needs to setup each thread that - * calls Windows APIs. It means that in winelib builds, we can't let standard C++ - * library create threads and need to use Wine for that instead. We use a thin wrapper - * around Windows thread functions so that the rest of code just has to use - * dxvk::thread class instead of std::thread. - */ - class ThreadFn : public RcObject { - using Proc = std::function<void()>; - public: - ThreadFn(Proc&& proc) - : m_proc(std::move(proc)) { - // Reference for the thread function - this->incRef(); + using ThreadProc = std::function<void()>; - m_handle = ::CreateThread(nullptr, 0x100000, - ThreadFn::threadProc, this, STACK_SIZE_PARAM_IS_A_RESERVATION, - nullptr); - - if (m_handle == nullptr) - throw DxvkError("Failed to create thread"); - } - ~ThreadFn() { - if (this->joinable()) - std::terminate(); - } - - void detach() { - ::CloseHandle(m_handle); - m_handle = nullptr; - } + /** + * \brief Thread object + */ + struct ThreadData { + ThreadData(ThreadProc&& proc_) + : proc(std::move(proc_)) { } - void join() { - if(::WaitForSingleObjectEx(m_handle, INFINITE, FALSE) == WAIT_FAILED) - throw DxvkError("Failed to join thread"); - this->detach(); + ~ThreadData() { + if (handle) + CloseHandle(handle); } - bool joinable() const { - return m_handle != nullptr; - } + HANDLE handle = nullptr; + DWORD id = 0; + std::atomic<uint32_t> refs = { 2u }; + ThreadProc proc; - void set_priority(ThreadPriority priority) { - int32_t value; - switch (priority) { - default: - case ThreadPriority::Normal: value = THREAD_PRIORITY_NORMAL; break; - case ThreadPriority::Lowest: value = THREAD_PRIORITY_LOWEST; break; - } - ::SetThreadPriority(m_handle, int32_t(value)); + void decRef() { + if (refs.fetch_sub(1, std::memory_order_release) == 1) + delete this; } - - private: - - Proc m_proc; - HANDLE m_handle; - - static DWORD WINAPI threadProc(void *arg) { - auto thread = reinterpret_cast<ThreadFn*>(arg); - thread->m_proc(); - thread->decRef(); - return 0; - } - }; /** - * \brief RAII thread wrapper - * - * Wrapper for \c ThreadFn that can be used - * as a drop-in replacement for \c std::thread. + * \brief Thread wrapper + * + * Drop-in replacement for std::thread + * using plain win32 threads. */ class thread { public: + using id = uint32_t; + using native_handle_type = HANDLE; + thread() { } - explicit thread(std::function<void()>&& func) - : m_thread(new ThreadFn(std::move(func))) { } + explicit thread(ThreadProc&& proc); + + ~thread(); thread(thread&& other) - : m_thread(std::move(other.m_thread)) { } + : m_data(std::exchange(other.m_data, nullptr)) { } thread& operator = (thread&& other) { - m_thread = std::move(other.m_thread); + if (m_data) + m_data->decRef(); + + m_data = std::exchange(other.m_data, nullptr); return *this; } void detach() { - m_thread->detach(); + m_data->decRef(); + m_data = nullptr; } - void join() { - m_thread->join(); + bool joinable() const { + return m_data != nullptr; } - bool joinable() const { - return m_thread != nullptr - && m_thread->joinable(); + id get_id() const { + return joinable() ? m_data->id : id(); } - void set_priority(ThreadPriority priority) { - m_thread->set_priority(priority); + native_handle_type native_handle() const { + return joinable() ? m_data->handle : native_handle_type(); } - - static uint32_t hardware_concurrency() { - SYSTEM_INFO info = { }; - ::GetSystemInfo(&info); - return info.dwNumberOfProcessors; + + void swap(thread& other) { + std::swap(m_data, other.m_data); } + void join(); + + void set_priority(ThreadPriority priority); + + static uint32_t hardware_concurrency(); + private: - Rc<ThreadFn> m_thread; + ThreadData* m_data = nullptr; + + static DWORD WINAPI threadProc(void* arg); }; @@ -153,8 +124,8 @@ namespace dxvk { SwitchToThread(); } - inline uint32_t get_id() { - return uint32_t(GetCurrentThreadId()); + inline thread::id get_id() { + return thread::id(GetCurrentThreadId()); } bool isInModuleDetachment(); |