2018-07-16 18:45:54 +02:00
|
|
|
#pragma once
|
|
|
|
|
2018-07-21 12:51:50 +02:00
|
|
|
#include <functional>
|
|
|
|
|
2018-07-16 18:45:54 +02:00
|
|
|
#include "util_error.h"
|
|
|
|
|
2018-07-21 12:43:33 +02:00
|
|
|
#include "./com/com_include.h"
|
2018-07-16 18:45:54 +02:00
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
#include "./rc/util_rc.h"
|
|
|
|
#include "./rc/util_rc_ptr.h"
|
|
|
|
|
2018-07-16 18:45:54 +02:00
|
|
|
namespace dxvk {
|
2018-07-22 01:28:35 +02:00
|
|
|
|
2018-07-21 12:51:50 +02:00
|
|
|
/**
|
|
|
|
* \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.
|
|
|
|
*/
|
2018-07-22 01:28:35 +02:00
|
|
|
class ThreadFn : public RcObject {
|
|
|
|
using Proc = std::function<void()>;
|
2018-07-16 18:45:54 +02:00
|
|
|
public:
|
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
ThreadFn(Proc&& proc)
|
|
|
|
: m_proc(std::move(proc)) {
|
2018-07-22 02:24:04 +02:00
|
|
|
// Reference for the thread function
|
|
|
|
this->incRef();
|
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
m_handle = ::CreateThread(nullptr, 0,
|
|
|
|
ThreadFn::threadProc, this, 0, nullptr);
|
|
|
|
|
|
|
|
if (m_handle == nullptr)
|
2018-07-21 12:51:50 +02:00
|
|
|
throw DxvkError("Failed to create thread");
|
|
|
|
}
|
2018-07-16 18:45:54 +02:00
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
~ThreadFn() {
|
|
|
|
if (this->joinable())
|
|
|
|
std::terminate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void detach() {
|
|
|
|
::CloseHandle(m_handle);
|
|
|
|
m_handle = nullptr;
|
2018-07-16 18:45:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void join() {
|
2018-07-22 01:28:35 +02:00
|
|
|
if(::WaitForSingleObjectEx(m_handle, INFINITE, FALSE) == WAIT_FAILED)
|
|
|
|
throw DxvkError("Failed to join thread");
|
|
|
|
this->detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool joinable() const {
|
|
|
|
return m_handle != nullptr;
|
2018-07-16 18:45:54 +02:00
|
|
|
}
|
2018-07-21 12:51:50 +02:00
|
|
|
|
|
|
|
private:
|
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
Proc m_proc;
|
|
|
|
HANDLE m_handle;
|
2018-07-21 12:51:50 +02:00
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
static DWORD WINAPI threadProc(void *arg) {
|
2018-07-22 02:24:04 +02:00
|
|
|
auto thread = reinterpret_cast<ThreadFn*>(arg);
|
|
|
|
thread->m_proc();
|
|
|
|
thread->decRef();
|
2018-07-21 12:51:50 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-16 18:45:54 +02:00
|
|
|
};
|
|
|
|
|
2018-07-22 01:28:35 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \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<void()>&& 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<ThreadFn> m_thread;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-07-16 18:45:54 +02:00
|
|
|
namespace this_thread {
|
|
|
|
inline void yield() {
|
|
|
|
Sleep(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|