Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/doitsujin/dxvk.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Rebohle <philip.rebohle@tu-dortmund.de>2023-04-25 12:49:17 +0300
committerPhilip Rebohle <philip.rebohle@tu-dortmund.de>2023-04-25 12:53:58 +0300
commit5a1980da617d51987821c82750cb2ea96d8067c8 (patch)
tree78337487c3b7360d672025d9df1471ca6cf245ca
parente074d83d0b502961604ea00c2ad24508bf33a1a2 (diff)
[dxvk] Rewrite thread wrapperthread-rework
Addresses some issues raised in #3378.
-rw-r--r--src/util/thread.cpp76
-rw-r--r--src/util/thread.h139
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();