mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
Initial commit
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
#include "common/threads.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono> // IWYU pragma: keep
|
||||
#include <condition_variable> // IWYU pragma: keep
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
#define KYTY_WIN_CS
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#ifdef KYTY_WIN_CS
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
// IWYU pragma: no_include <winbase.h>
|
||||
constexpr DWORD KYTY_CS_SPIN_COUNT = 4000;
|
||||
|
||||
#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
|
||||
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
|
||||
#endif
|
||||
|
||||
static void SleepHighResolution100ns(uint64_t units_100ns) {
|
||||
if (units_100ns == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (units_100ns <= 10000) {
|
||||
LARGE_INTEGER frequency {};
|
||||
LARGE_INTEGER start {};
|
||||
if (QueryPerformanceFrequency(&frequency) != 0 && QueryPerformanceCounter(&start) != 0 &&
|
||||
frequency.QuadPart > 0) {
|
||||
const auto wait_ticks =
|
||||
static_cast<LONGLONG>((static_cast<long double>(units_100ns) *
|
||||
static_cast<long double>(frequency.QuadPart)) /
|
||||
10000000.0L);
|
||||
const auto deadline = start.QuadPart + std::max<LONGLONG>(wait_ticks, 1);
|
||||
LARGE_INTEGER now {};
|
||||
do {
|
||||
if (QueryPerformanceCounter(&now) == 0) {
|
||||
break;
|
||||
}
|
||||
YieldProcessor();
|
||||
} while (now.QuadPart < deadline);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
thread_local HANDLE timer = CreateWaitableTimerExW(
|
||||
nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
if (timer == nullptr) {
|
||||
thread_local HANDLE fallback_timer = CreateWaitableTimerW(nullptr, TRUE, nullptr);
|
||||
timer = fallback_timer;
|
||||
}
|
||||
|
||||
if (timer != nullptr) {
|
||||
LARGE_INTEGER due_time {};
|
||||
due_time.QuadPart = -static_cast<LONGLONG>(units_100ns);
|
||||
if (SetWaitableTimerEx(timer, &due_time, 0, nullptr, nullptr, nullptr, 0) != 0) {
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(units_100ns * 100));
|
||||
}
|
||||
|
||||
// IWYU pragma: no_include <minwindef.h>
|
||||
// IWYU pragma: no_include <synchapi.h>
|
||||
// IWYU pragma: no_include <minwinbase.h>
|
||||
// IWYU pragma: no_include <__mutex_base>
|
||||
// IWYU pragma: no_include <__threading_support>
|
||||
// IWYU pragma: no_include <errhandlingapi.h>
|
||||
// IWYU pragma: no_include <winerror.h>
|
||||
|
||||
using InitializeConditionVariable_func_t = /*WINBASEAPI*/ VOID WINAPI (*)(PCONDITION_VARIABLE);
|
||||
using WakeConditionVariable_func_t = /*WINBASEAPI*/ VOID WINAPI (*)(PCONDITION_VARIABLE);
|
||||
using WakeAllConditionVariable_func_t = /*WINBASEAPI*/ VOID WINAPI (*)(PCONDITION_VARIABLE);
|
||||
using SleepConditionVariableCS_func_t = /*WINBASEAPI*/ BOOL WINAPI (*)(PCONDITION_VARIABLE,
|
||||
PCRITICAL_SECTION, DWORD);
|
||||
|
||||
static InitializeConditionVariable_func_t ResolveInitializeConditionVariable() {
|
||||
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
|
||||
{
|
||||
return reinterpret_cast<InitializeConditionVariable_func_t>(
|
||||
GetProcAddress(h, "InitializeConditionVariable"));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static WakeConditionVariable_func_t ResolveWakeConditionVariable() {
|
||||
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
|
||||
{
|
||||
return reinterpret_cast<WakeConditionVariable_func_t>(
|
||||
GetProcAddress(h, "WakeConditionVariable"));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static WakeAllConditionVariable_func_t ResolveWakeAllConditionVariable() {
|
||||
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
|
||||
{
|
||||
return reinterpret_cast<WakeAllConditionVariable_func_t>(
|
||||
GetProcAddress(h, "WakeAllConditionVariable"));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static SleepConditionVariableCS_func_t ResolveSleepConditionVariableCS() {
|
||||
if (HMODULE h = GetModuleHandle("KernelBase"); h != nullptr) // @suppress("Invalid arguments")
|
||||
{
|
||||
return reinterpret_cast<SleepConditionVariableCS_func_t>(
|
||||
GetProcAddress(h, "SleepConditionVariableCS"));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
using thread_id_t = std::thread::id;
|
||||
|
||||
struct MutexPrivate {
|
||||
#ifdef KYTY_WIN_CS
|
||||
MutexPrivate() { InitializeCriticalSectionAndSpinCount(&m_cs, KYTY_CS_SPIN_COUNT); }
|
||||
~MutexPrivate() { DeleteCriticalSection(&m_cs); }
|
||||
KYTY_CLASS_NO_COPY(MutexPrivate);
|
||||
CRITICAL_SECTION m_cs {};
|
||||
#else
|
||||
std::recursive_mutex m_mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct CondVarPrivate {
|
||||
#ifdef KYTY_WIN_CS
|
||||
CondVarPrivate() {
|
||||
static auto func = ResolveInitializeConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&m_cv);
|
||||
}
|
||||
~CondVarPrivate() = default;
|
||||
KYTY_CLASS_NO_COPY(CondVarPrivate);
|
||||
CONDITION_VARIABLE m_cv {};
|
||||
#else
|
||||
std::condition_variable_any m_cv;
|
||||
#endif
|
||||
};
|
||||
|
||||
static std::recursive_mutex g_cond_waiters_mutex;
|
||||
static std::vector<std::pair<int, CondVarPrivate*>> g_cond_waiters;
|
||||
static wait_poll_func_t g_cond_wait_poll_callback = nullptr;
|
||||
|
||||
static void WakeCondVar(CondVarPrivate* cond_var) {
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveWakeAllConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&cond_var->m_cv);
|
||||
#else
|
||||
cond_var->m_cv.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void RegisterCondWaiter(CondVarPrivate* cond_var) {
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
g_cond_waiters.emplace_back(Thread::GetThreadIdUnique(), cond_var);
|
||||
}
|
||||
|
||||
static void UnregisterCondWaiter(CondVarPrivate* cond_var) {
|
||||
const auto thread_id = Thread::GetThreadIdUnique();
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
|
||||
const auto it = std::find_if(g_cond_waiters.begin(), g_cond_waiters.end(),
|
||||
[thread_id, cond_var](const auto& waiter) {
|
||||
return waiter.first == thread_id && waiter.second == cond_var;
|
||||
});
|
||||
if (it != g_cond_waiters.end()) {
|
||||
g_cond_waiters.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadPrivate {
|
||||
ThreadPrivate(thread_func_t f, void* a): func(f), arg(a), m_thread(&Run, this) {}
|
||||
|
||||
static void Run(ThreadPrivate* t) {
|
||||
t->unique_id = Thread::GetThreadIdUnique();
|
||||
t->started = true;
|
||||
t->func(t->arg);
|
||||
}
|
||||
|
||||
thread_func_t func;
|
||||
void* arg;
|
||||
std::atomic_bool finished = false;
|
||||
std::atomic_bool auto_delete = false;
|
||||
std::atomic_bool started = false;
|
||||
int unique_id = 0;
|
||||
std::thread m_thread;
|
||||
};
|
||||
|
||||
static thread_id_t g_main_thread;
|
||||
static int g_main_thread_int;
|
||||
static std::atomic<int> g_thread_counter = 0;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Threads) {
|
||||
g_main_thread = std::this_thread::get_id();
|
||||
g_main_thread_int = Thread::GetThreadIdUnique();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Threads) {}
|
||||
|
||||
Thread::Thread(thread_func_t func, void* arg)
|
||||
: m_thread(std::make_unique<ThreadPrivate>(func, arg)) {
|
||||
while (!m_thread->started) {
|
||||
Common::Thread::SleepMicro(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Thread::~Thread() {
|
||||
EXIT_IF(!m_thread->finished && !m_thread->auto_delete);
|
||||
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
void Thread::Join() {
|
||||
EXIT_IF(m_thread->finished || m_thread->auto_delete);
|
||||
|
||||
m_thread->m_thread.join();
|
||||
|
||||
m_thread->finished = true;
|
||||
}
|
||||
|
||||
void Thread::Detach() {
|
||||
EXIT_IF(m_thread->finished || m_thread->auto_delete);
|
||||
|
||||
m_thread->auto_delete = true;
|
||||
m_thread->m_thread.detach();
|
||||
}
|
||||
|
||||
void Thread::Sleep(uint32_t millis) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
|
||||
}
|
||||
|
||||
void Thread::SleepMicro(uint32_t micros) {
|
||||
#ifdef KYTY_WIN_CS
|
||||
SleepHighResolution100ns(static_cast<uint64_t>(micros) * 10);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::SleepNano(uint64_t nanos) {
|
||||
#ifdef KYTY_WIN_CS
|
||||
SleepHighResolution100ns((nanos + 99) / 100);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Thread::IsMainThread() {
|
||||
return g_main_thread == std::this_thread::get_id();
|
||||
}
|
||||
|
||||
std::string Thread::GetId() const {
|
||||
std::stringstream ss;
|
||||
ss << m_thread->m_thread.get_id();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int Thread::GetUniqueId() const {
|
||||
return m_thread->unique_id;
|
||||
}
|
||||
|
||||
std::string Thread::GetThreadId() {
|
||||
std::stringstream ss;
|
||||
ss << std::this_thread::get_id();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Mutex::Mutex(): m_mutex(std::make_unique<MutexPrivate>()) {}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
m_mutex.reset();
|
||||
}
|
||||
|
||||
void Mutex::Lock() {
|
||||
#ifdef KYTY_WIN_CS
|
||||
EnterCriticalSection(&m_mutex->m_cs);
|
||||
#else
|
||||
m_mutex->m_mutex.lock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Unlock() {
|
||||
#ifdef KYTY_WIN_CS
|
||||
LeaveCriticalSection(&m_mutex->m_cs);
|
||||
#else
|
||||
m_mutex->m_mutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Mutex::TryLock() {
|
||||
#ifdef KYTY_WIN_CS
|
||||
return (TryEnterCriticalSection(&m_mutex->m_cs) != 0);
|
||||
#else
|
||||
return m_mutex->m_mutex.try_lock();
|
||||
#endif
|
||||
}
|
||||
|
||||
CondVar::CondVar(): m_cond_var(std::make_unique<CondVarPrivate>()) {}
|
||||
|
||||
CondVar::~CondVar() {
|
||||
m_cond_var.reset();
|
||||
}
|
||||
|
||||
void CondVar::Wait(Mutex* mutex) {
|
||||
RegisterCondWaiter(m_cond_var.get());
|
||||
#ifndef KYTY_WIN_CS
|
||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||
#endif
|
||||
auto poll_callback = [&] {
|
||||
auto* callback = g_cond_wait_poll_callback;
|
||||
if (callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
#if defined(KYTY_WIN_CS)
|
||||
LeaveCriticalSection(&mutex->m_mutex->m_cs);
|
||||
callback();
|
||||
EnterCriticalSection(&mutex->m_mutex->m_cs);
|
||||
#else
|
||||
cpp_lock.unlock();
|
||||
callback();
|
||||
cpp_lock.lock();
|
||||
#endif
|
||||
};
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveSleepConditionVariableCS();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
if (g_cond_wait_poll_callback == nullptr) {
|
||||
func(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, INFINITE);
|
||||
} else {
|
||||
if (func(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, 10) == 0 &&
|
||||
GetLastError() == ERROR_TIMEOUT) {
|
||||
poll_callback();
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (g_cond_wait_poll_callback == nullptr) {
|
||||
m_cond_var->m_cv.wait(cpp_lock);
|
||||
} else {
|
||||
if (m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(10000)) ==
|
||||
std::cv_status::timeout) {
|
||||
poll_callback();
|
||||
}
|
||||
}
|
||||
cpp_lock.release();
|
||||
#endif
|
||||
UnregisterCondWaiter(m_cond_var.get());
|
||||
}
|
||||
|
||||
void CondVar::SetWaitPollCallback(wait_poll_func_t callback) {
|
||||
g_cond_wait_poll_callback = callback;
|
||||
}
|
||||
|
||||
bool CondVar::WaitFor(Mutex* mutex, uint32_t micros) {
|
||||
bool ok = false;
|
||||
RegisterCondWaiter(m_cond_var.get());
|
||||
#ifndef KYTY_WIN_CS
|
||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||
#endif
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveSleepConditionVariableCS();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
ok = !(func(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, (micros < 1000 ? 1 : micros / 1000)) ==
|
||||
0 &&
|
||||
GetLastError() == ERROR_TIMEOUT);
|
||||
#else
|
||||
ok = (m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros)) ==
|
||||
std::cv_status::no_timeout);
|
||||
cpp_lock.release();
|
||||
#endif
|
||||
UnregisterCondWaiter(m_cond_var.get());
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CondVar::Signal() {
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveWakeConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&m_cond_var->m_cv);
|
||||
#else
|
||||
m_cond_var->m_cv.notify_one();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CondVar::SignalAll() {
|
||||
WakeCondVar(m_cond_var.get());
|
||||
}
|
||||
|
||||
void CondVar::SignalThread(int thread_id) {
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
for (const auto& waiter: g_cond_waiters) {
|
||||
if (waiter.first == thread_id) {
|
||||
WakeCondVar(waiter.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Thread::GetThreadIdUnique() {
|
||||
static thread_local int tid = ++g_thread_counter;
|
||||
return tid;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
Reference in New Issue
Block a user