mirror of https://github.com/doitsujin/dxvk
parent
b44c5bbd18
commit
5c8ed491ab
|
@ -1,10 +1,82 @@
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "util_likely.h"
|
#include "util_likely.h"
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#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 {
|
namespace dxvk::this_thread {
|
||||||
|
|
||||||
bool isInModuleDetachment() {
|
bool isInModuleDetachment() {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "util_error.h"
|
#include "util_error.h"
|
||||||
|
|
||||||
|
@ -24,126 +25,96 @@ namespace dxvk {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
using ThreadProc = std::function<void()>;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Thread helper class
|
* \brief Thread object
|
||||||
*
|
|
||||||
* 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 {
|
struct ThreadData {
|
||||||
using Proc = std::function<void()>;
|
ThreadData(ThreadProc&& proc_)
|
||||||
public:
|
: proc(std::move(proc_)) { }
|
||||||
|
|
||||||
ThreadFn(Proc&& proc)
|
~ThreadData() {
|
||||||
: m_proc(std::move(proc)) {
|
if (handle)
|
||||||
// Reference for the thread function
|
CloseHandle(handle);
|
||||||
this->incRef();
|
|
||||||
|
|
||||||
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() {
|
HANDLE handle = nullptr;
|
||||||
if (this->joinable())
|
DWORD id = 0;
|
||||||
std::terminate();
|
std::atomic<uint32_t> refs = { 2u };
|
||||||
|
ThreadProc proc;
|
||||||
|
|
||||||
|
void decRef() {
|
||||||
|
if (refs.fetch_sub(1, std::memory_order_release) == 1)
|
||||||
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detach() {
|
|
||||||
::CloseHandle(m_handle);
|
|
||||||
m_handle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void join() {
|
|
||||||
if(::WaitForSingleObjectEx(m_handle, INFINITE, FALSE) == WAIT_FAILED)
|
|
||||||
throw DxvkError("Failed to join thread");
|
|
||||||
this->detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool joinable() const {
|
|
||||||
return m_handle != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
* \brief Thread wrapper
|
||||||
*
|
*
|
||||||
* Wrapper for \c ThreadFn that can be used
|
* Drop-in replacement for std::thread
|
||||||
* as a drop-in replacement for \c std::thread.
|
* using plain win32 threads.
|
||||||
*/
|
*/
|
||||||
class thread {
|
class thread {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
using id = uint32_t;
|
||||||
|
using native_handle_type = HANDLE;
|
||||||
|
|
||||||
thread() { }
|
thread() { }
|
||||||
|
|
||||||
explicit thread(std::function<void()>&& func)
|
explicit thread(ThreadProc&& proc);
|
||||||
: m_thread(new ThreadFn(std::move(func))) { }
|
|
||||||
|
~thread();
|
||||||
|
|
||||||
thread(thread&& other)
|
thread(thread&& other)
|
||||||
: m_thread(std::move(other.m_thread)) { }
|
: m_data(std::exchange(other.m_data, nullptr)) { }
|
||||||
|
|
||||||
thread& operator = (thread&& other) {
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detach() {
|
void detach() {
|
||||||
m_thread->detach();
|
m_data->decRef();
|
||||||
}
|
m_data = nullptr;
|
||||||
|
|
||||||
void join() {
|
|
||||||
m_thread->join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool joinable() const {
|
bool joinable() const {
|
||||||
return m_thread != nullptr
|
return m_data != nullptr;
|
||||||
&& m_thread->joinable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_priority(ThreadPriority priority) {
|
id get_id() const {
|
||||||
m_thread->set_priority(priority);
|
return joinable() ? m_data->id : id();
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t hardware_concurrency() {
|
native_handle_type native_handle() const {
|
||||||
SYSTEM_INFO info = { };
|
return joinable() ? m_data->handle : native_handle_type();
|
||||||
::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:
|
private:
|
||||||
|
|
||||||
Rc<ThreadFn> m_thread;
|
ThreadData* m_data = nullptr;
|
||||||
|
|
||||||
|
static DWORD WINAPI threadProc(void* arg);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,8 +124,8 @@ namespace dxvk {
|
||||||
SwitchToThread();
|
SwitchToThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t get_id() {
|
inline thread::id get_id() {
|
||||||
return uint32_t(GetCurrentThreadId());
|
return thread::id(GetCurrentThreadId());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInModuleDetachment();
|
bool isInModuleDetachment();
|
||||||
|
|
Loading…
Reference in New Issue