#pragma once #include #include #include #include #include #include "util_error.h" #include "./com/com_include.h" #include "./rc/util_rc.h" #include "./rc/util_rc_ptr.h" namespace dxvk { /** * \brief Thread priority */ enum class ThreadPriority : int32_t { Normal, Lowest, }; #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; public: ThreadFn(Proc&& proc) : m_proc(std::move(proc)) { // Reference for the thread function 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() { if (this->joinable()) std::terminate(); } 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(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. */ 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(); } void set_priority(ThreadPriority priority) { m_thread->set_priority(priority); } static uint32_t hardware_concurrency() { SYSTEM_INFO info = { }; ::GetSystemInfo(&info); return info.dwNumberOfProcessors; } private: Rc m_thread; }; namespace this_thread { inline void yield() { SwitchToThread(); } inline uint32_t get_id() { return uint32_t(GetCurrentThreadId()); } bool isInModuleDetachment(); } /** * \brief SRW-based mutex implementation * * Drop-in replacement for \c std::mutex that uses Win32 * SRW locks, which are implemented with \c futex in wine. */ class mutex { public: using native_handle_type = PSRWLOCK; mutex() { } mutex(const mutex&) = delete; mutex& operator = (const mutex&) = delete; void lock() { AcquireSRWLockExclusive(&m_lock); } void unlock() { ReleaseSRWLockExclusive(&m_lock); } bool try_lock() { return TryAcquireSRWLockExclusive(&m_lock); } native_handle_type native_handle() { return &m_lock; } private: SRWLOCK m_lock = SRWLOCK_INIT; }; /** * \brief Recursive mutex implementation * * Drop-in replacement for \c std::recursive_mutex that * uses Win32 critical sections. */ class recursive_mutex { public: using native_handle_type = PCRITICAL_SECTION; recursive_mutex() { InitializeCriticalSection(&m_lock); } ~recursive_mutex() { DeleteCriticalSection(&m_lock); } recursive_mutex(const recursive_mutex&) = delete; recursive_mutex& operator = (const recursive_mutex&) = delete; void lock() { EnterCriticalSection(&m_lock); } void unlock() { LeaveCriticalSection(&m_lock); } bool try_lock() { return TryEnterCriticalSection(&m_lock); } native_handle_type native_handle() { return &m_lock; } private: CRITICAL_SECTION m_lock; }; /** * \brief SRW-based condition variable implementation * * Drop-in replacement for \c std::condition_variable that * uses Win32 condition variables on SRW locks. */ class condition_variable { public: using native_handle_type = PCONDITION_VARIABLE; condition_variable() { InitializeConditionVariable(&m_cond); } condition_variable(condition_variable&) = delete; condition_variable& operator = (condition_variable&) = delete; void notify_one() { WakeConditionVariable(&m_cond); } void notify_all() { WakeAllConditionVariable(&m_cond); } void wait(std::unique_lock& lock) { auto srw = lock.mutex()->native_handle(); SleepConditionVariableSRW(&m_cond, srw, INFINITE, 0); } template void wait(std::unique_lock& lock, Predicate pred) { while (!pred()) wait(lock); } template std::cv_status wait_until(std::unique_lock& lock, const std::chrono::time_point& time) { auto now = Clock::now(); return (now < time) ? wait_for(lock, now - time) : std::cv_status::timeout; } template bool wait_until(std::unique_lock& lock, const std::chrono::time_point& time, Predicate pred) { if (pred()) return true; auto now = Clock::now(); return now < time && wait_for(lock, now - time, pred); } template std::cv_status wait_for(std::unique_lock& lock, const std::chrono::duration& timeout) { auto ms = std::chrono::duration_cast(timeout); auto srw = lock.mutex()->native_handle(); return SleepConditionVariableSRW(&m_cond, srw, ms.count(), 0) ? std::cv_status::no_timeout : std::cv_status::timeout; } template bool wait_for(std::unique_lock& lock, const std::chrono::duration& timeout, Predicate pred) { bool result = pred(); if (!result && wait_for(lock, timeout) == std::cv_status::no_timeout) result = pred(); return result; } native_handle_type native_handle() { return &m_cond; } private: CONDITION_VARIABLE m_cond; }; #else class thread : public std::thread { public: using std::thread::thread; void set_priority(ThreadPriority priority) { ::sched_param param = {}; int32_t policy; switch (priority) { default: case ThreadPriority::Normal: policy = SCHED_OTHER; break; case ThreadPriority::Lowest: policy = SCHED_IDLE; break; } ::pthread_setschedparam(this->native_handle(), policy, ¶m); } }; using mutex = std::mutex; using recursive_mutex = std::recursive_mutex; using condition_variable = std::condition_variable; namespace this_thread { inline void yield() { std::this_thread::yield(); } uint32_t get_id(); inline bool isInModuleDetachment() { return false; } } #endif }