mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2026-04-27 04:41:39 +00:00
Cross platform thread implementation. (#41)
* Cross-platform thread implementation. * Put set thread name calls behind a Win32 macro.
This commit is contained in:
parent
c23db5b746
commit
afce26fc35
6 changed files with 164 additions and 97 deletions
|
|
@ -27,7 +27,7 @@ GuestThreadContext::GuestThreadContext(uint32_t cpuNumber)
|
|||
*(thread + 0x10C) = cpuNumber;
|
||||
|
||||
*(uint32_t*)(thread + PCR_SIZE + 0x10) = 0xFFFFFFFF; // that one TLS entry that felt quirky
|
||||
*(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = ByteSwap(GetCurrentThreadId()); // thread id
|
||||
*(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = ByteSwap(GuestThread::GetCurrentThreadId()); // thread id
|
||||
|
||||
ppcContext.fn = (uint8_t*)g_codeCache.bucket;
|
||||
ppcContext.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer
|
||||
|
|
@ -42,42 +42,82 @@ GuestThreadContext::~GuestThreadContext()
|
|||
g_userHeap.Free(thread);
|
||||
}
|
||||
|
||||
DWORD GuestThread::Start(uint32_t function)
|
||||
static void GuestThreadFunc(GuestThreadHandle* hThread)
|
||||
{
|
||||
const GuestThreadParameter parameter{ function };
|
||||
return Start(parameter);
|
||||
hThread->suspended.wait(true);
|
||||
GuestThread::Start(hThread->params);
|
||||
}
|
||||
|
||||
DWORD GuestThread::Start(const GuestThreadParameter& parameter)
|
||||
GuestThreadHandle::GuestThreadHandle(const GuestThreadParams& params)
|
||||
: params(params), suspended((params.flags & 0x1) != 0), thread(GuestThreadFunc, this)
|
||||
{
|
||||
const auto procMask = (uint8_t)(parameter.flags >> 24);
|
||||
}
|
||||
|
||||
GuestThreadHandle::~GuestThreadHandle()
|
||||
{
|
||||
if (thread.joinable())
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void GuestThreadHandle::Wait(uint32_t timeout)
|
||||
{
|
||||
assert(timeout == INFINITE);
|
||||
|
||||
if (thread.joinable())
|
||||
thread.join();
|
||||
}
|
||||
|
||||
uint32_t GuestThread::Start(const GuestThreadParams& params)
|
||||
{
|
||||
const auto procMask = (uint8_t)(params.flags >> 24);
|
||||
const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask);
|
||||
|
||||
GuestThreadContext ctx(cpuNumber);
|
||||
ctx.ppcContext.r3.u64 = parameter.value;
|
||||
ctx.ppcContext.r3.u64 = params.value;
|
||||
|
||||
GuestCode::Run(g_codeCache.Find(parameter.function), &ctx.ppcContext, g_memory.Translate(0));
|
||||
GuestCode::Run(g_codeCache.Find(params.function), &ctx.ppcContext, g_memory.Translate(0));
|
||||
|
||||
return (DWORD)ctx.ppcContext.r3.u64;
|
||||
return ctx.ppcContext.r3.u32;
|
||||
}
|
||||
|
||||
DWORD HostThreadStart(void* pParameter)
|
||||
static uint32_t GetThreadId(const std::thread::id& id)
|
||||
{
|
||||
auto* parameter = static_cast<GuestThreadParameter*>(pParameter);
|
||||
const auto result = GuestThread::Start(*parameter);
|
||||
|
||||
delete parameter;
|
||||
return result;
|
||||
if constexpr (sizeof(id) == 4)
|
||||
return *reinterpret_cast<const uint32_t*>(&id);
|
||||
else
|
||||
return XXH32(&id, sizeof(id), 0);
|
||||
}
|
||||
|
||||
HANDLE GuestThread::Start(uint32_t function, uint32_t parameter, uint32_t flags, LPDWORD threadId)
|
||||
GuestThreadHandle* GuestThread::Start(const GuestThreadParams& params, uint32_t* threadId)
|
||||
{
|
||||
const auto hostCreationFlags = (flags & 1) != 0 ? CREATE_SUSPENDED : 0;
|
||||
//return CreateThread(nullptr, 0, Start, (void*)((uint64_t(parameter) << 32) | function), suspended ? CREATE_SUSPENDED : 0, threadId);
|
||||
return CreateThread(nullptr, 0, HostThreadStart, new GuestThreadParameter{ function, parameter, flags }, hostCreationFlags, threadId);
|
||||
auto hThread = CreateKernelObject<GuestThreadHandle>(params);
|
||||
|
||||
if (threadId != nullptr)
|
||||
*threadId = GetThreadId(hThread->thread.get_id());
|
||||
|
||||
return hThread;
|
||||
}
|
||||
|
||||
void GuestThread::SetThreadName(uint32_t id, const char* name)
|
||||
uint32_t GuestThread::GetCurrentThreadId()
|
||||
{
|
||||
return GetThreadId(std::this_thread::get_id());
|
||||
}
|
||||
|
||||
void GuestThread::SetLastError(uint32_t error)
|
||||
{
|
||||
auto* thread = (char*)g_memory.Translate(GetPPCContext()->r13.u32);
|
||||
if (*(uint32_t*)(thread + 0x150))
|
||||
{
|
||||
// Program doesn't want errors
|
||||
return;
|
||||
}
|
||||
|
||||
// TEB + 0x160 : Win32LastError
|
||||
*(uint32_t*)(thread + TEB_OFFSET + 0x160) = ByteSwap(error);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void GuestThread::SetThreadName(uint32_t threadId, const char* name)
|
||||
{
|
||||
#pragma pack(push,8)
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
|
@ -94,7 +134,7 @@ void GuestThread::SetThreadName(uint32_t id, const char* name)
|
|||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = id;
|
||||
info.dwThreadID = threadId;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
|
|
@ -105,41 +145,31 @@ void GuestThread::SetThreadName(uint32_t id, const char* name)
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
void GuestThread::SetLastError(DWORD error)
|
||||
{
|
||||
auto* thread = (char*)g_memory.Translate(GetPPCContext()->r13.u32);
|
||||
if (*(DWORD*)(thread + 0x150))
|
||||
{
|
||||
// Program doesn't want errors
|
||||
return;
|
||||
}
|
||||
|
||||
// TEB + 0x160 : Win32LastError
|
||||
*(DWORD*)(thread + TEB_OFFSET + 0x160) = ByteSwap(error);
|
||||
}
|
||||
|
||||
PPCContext* GuestThread::Invoke(uint32_t address)
|
||||
{
|
||||
auto* ctx = GetPPCContext();
|
||||
GuestCode::Run(g_codeCache.Find(address), ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetThreadNameImpl(uint32_t a1, uint32_t threadId, uint32_t* name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(threadId, (const char*)g_memory.Translate(ByteSwap(*name)));
|
||||
#endif
|
||||
}
|
||||
|
||||
int GetThreadPriorityImpl(uint32_t hThread)
|
||||
int GetThreadPriorityImpl(GuestThreadHandle* hThread)
|
||||
{
|
||||
return GetThreadPriority((HANDLE)hThread);
|
||||
#ifdef _WIN32
|
||||
return GetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle());
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
DWORD SetThreadIdealProcessorImpl(uint32_t hThread, DWORD dwIdealProcessor)
|
||||
uint32_t SetThreadIdealProcessorImpl(GuestThreadHandle* hThread, uint32_t dwIdealProcessor)
|
||||
{
|
||||
return SetThreadIdealProcessor((HANDLE)hThread, dwIdealProcessor);
|
||||
#ifdef _WIN32
|
||||
return SetThreadIdealProcessor(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), dwIdealProcessor);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
GUEST_FUNCTION_HOOK(sub_82DFA2E8, SetThreadNameImpl);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
struct PPCContext;
|
||||
struct GuestThreadParameter
|
||||
{
|
||||
uint32_t function;
|
||||
uint32_t value;
|
||||
uint32_t flags;
|
||||
};
|
||||
#include <kernel/xdm.h>
|
||||
|
||||
#define CURRENT_THREAD_HANDLE uint32_t(-2)
|
||||
|
||||
struct GuestThreadContext
|
||||
{
|
||||
|
|
@ -17,13 +13,34 @@ struct GuestThreadContext
|
|||
~GuestThreadContext();
|
||||
};
|
||||
|
||||
struct GuestThreadParams
|
||||
{
|
||||
uint32_t function;
|
||||
uint32_t value;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct GuestThreadHandle : KernelObject
|
||||
{
|
||||
GuestThreadParams params;
|
||||
std::atomic<bool> suspended;
|
||||
std::thread thread;
|
||||
|
||||
GuestThreadHandle(const GuestThreadParams& params);
|
||||
~GuestThreadHandle() override;
|
||||
|
||||
void Wait(uint32_t timeout) override;
|
||||
};
|
||||
|
||||
struct GuestThread
|
||||
{
|
||||
static DWORD Start(uint32_t function);
|
||||
static DWORD Start(const GuestThreadParameter& parameter);
|
||||
static HANDLE Start(uint32_t function, uint32_t parameter, uint32_t flags, LPDWORD threadId);
|
||||
static uint32_t Start(const GuestThreadParams& params);
|
||||
static GuestThreadHandle* Start(const GuestThreadParams& params, uint32_t* threadId);
|
||||
|
||||
static void SetThreadName(uint32_t id, const char* name);
|
||||
static void SetLastError(DWORD error);
|
||||
static PPCContext* Invoke(uint32_t address);
|
||||
static uint32_t GetCurrentThreadId();
|
||||
static void SetLastError(uint32_t error);
|
||||
|
||||
#ifdef _WIN32
|
||||
static void SetThreadName(uint32_t threadId, const char* name);
|
||||
#endif
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4047,8 +4047,9 @@ static void ProcSetPixelShader(const RenderCommand& cmd)
|
|||
|
||||
static std::thread g_renderThread([]
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(GetCurrentThreadId(), "Render Thread");
|
||||
|
||||
#endif
|
||||
RenderCommand commands[32];
|
||||
|
||||
while (true)
|
||||
|
|
@ -4857,7 +4858,9 @@ static moodycamel::BlockingConcurrentQueue<PipelineStateQueueItem> g_pipelineSta
|
|||
|
||||
static void PipelineCompilerThread()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(GetCurrentThreadId(), "Pipeline Compiler Thread");
|
||||
#endif
|
||||
std::unique_ptr<GuestThreadContext> ctx;
|
||||
|
||||
while (true)
|
||||
|
|
@ -5508,8 +5511,9 @@ static bool CheckMadeAll(const T& modelData)
|
|||
|
||||
static void ModelConsumerThread()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(GetCurrentThreadId(), "Model Consumer Thread");
|
||||
|
||||
#endif
|
||||
std::vector<boost::shared_ptr<Hedgehog::Database::CDatabaseData>> localPendingDataQueue;
|
||||
std::unique_ptr<GuestThreadContext> ctx;
|
||||
|
||||
|
|
|
|||
|
|
@ -256,15 +256,25 @@ uint32_t FscSetCacheElementCount()
|
|||
|
||||
DWORD NtWaitForSingleObjectEx(DWORD Handle, DWORD WaitMode, DWORD Alertable, XLPQWORD Timeout)
|
||||
{
|
||||
const auto status = WaitForSingleObjectEx((HANDLE)Handle, GuestTimeoutToMilliseconds(Timeout), Alertable);
|
||||
uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == 0 || timeout == INFINITE);
|
||||
|
||||
if (status == WAIT_IO_COMPLETION)
|
||||
if (IsKernelObject(Handle))
|
||||
{
|
||||
return STATUS_USER_APC;
|
||||
GetKernelObject(Handle)->Wait(timeout);
|
||||
}
|
||||
else if (status)
|
||||
else
|
||||
{
|
||||
return STATUS_ALERTED;
|
||||
const auto status = WaitForSingleObjectEx((HANDLE)Handle, timeout, Alertable);
|
||||
|
||||
if (status == WAIT_IO_COMPLETION)
|
||||
{
|
||||
return STATUS_USER_APC;
|
||||
}
|
||||
else if (status)
|
||||
{
|
||||
return STATUS_ALERTED;
|
||||
}
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
|
@ -474,8 +484,9 @@ void ObDereferenceObject()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
void KeSetBasePriorityThread(uint32_t thread, int priority)
|
||||
void KeSetBasePriorityThread(GuestThreadHandle* hThread, int priority)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (priority == 16)
|
||||
{
|
||||
priority = 15;
|
||||
|
|
@ -485,7 +496,8 @@ void KeSetBasePriorityThread(uint32_t thread, int priority)
|
|||
priority = -15;
|
||||
}
|
||||
|
||||
SetThreadPriority((HANDLE)thread, priority);
|
||||
SetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), priority);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t ObReferenceObjectByHandle(uint32_t handle, uint32_t objectType, XLPDWORD object)
|
||||
|
|
@ -499,15 +511,12 @@ void KeQueryBasePriorityThread()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtSuspendThread(uint32_t hThread, uint32_t* suspendCount)
|
||||
uint32_t NtSuspendThread(GuestThreadHandle* hThread, uint32_t* suspendCount)
|
||||
{
|
||||
DWORD count = SuspendThread((HANDLE)hThread);
|
||||
assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE) && hThread->thread.get_id() == std::this_thread::get_id());
|
||||
|
||||
if (count == (DWORD)-1)
|
||||
return E_FAIL;
|
||||
|
||||
if (suspendCount != nullptr)
|
||||
*suspendCount = ByteSwap(count);
|
||||
hThread->suspended = true;
|
||||
hThread->suspended.wait(true);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
@ -890,29 +899,32 @@ DWORD KeWaitForSingleObject(XDISPATCHER_HEADER* Object, DWORD WaitReason, DWORD
|
|||
return WaitForSingleObjectEx(handle, timeout, Alertable);
|
||||
}
|
||||
|
||||
static thread_local std::vector<uint32_t> g_tlsValues;
|
||||
static std::vector<size_t> g_tlsFreeIndices;
|
||||
static size_t g_tlsNextIndex = 0;
|
||||
static Mutex g_tlsAllocationMutex;
|
||||
|
||||
static void KeTlsEnsureTlsCapacity(size_t index)
|
||||
static uint32_t& KeTlsGetValueRef(size_t index)
|
||||
{
|
||||
if (g_tlsValues.size() <= index)
|
||||
// Having this a global thread_local variable
|
||||
// for some reason crashes on boot in debug builds.
|
||||
thread_local std::vector<uint32_t> s_tlsValues;
|
||||
|
||||
if (s_tlsValues.size() <= index)
|
||||
{
|
||||
g_tlsValues.resize(index + 1, 0);
|
||||
s_tlsValues.resize(index + 1, 0);
|
||||
}
|
||||
|
||||
return s_tlsValues[index];
|
||||
}
|
||||
|
||||
uint32_t KeTlsGetValue(DWORD dwTlsIndex)
|
||||
{
|
||||
KeTlsEnsureTlsCapacity(dwTlsIndex);
|
||||
return g_tlsValues[dwTlsIndex];
|
||||
return KeTlsGetValueRef(dwTlsIndex);
|
||||
}
|
||||
|
||||
BOOL KeTlsSetValue(DWORD dwTlsIndex, DWORD lpTlsValue)
|
||||
{
|
||||
KeTlsEnsureTlsCapacity(dwTlsIndex);
|
||||
g_tlsValues[dwTlsIndex] = lpTlsValue;
|
||||
KeTlsGetValueRef(dwTlsIndex) = lpTlsValue;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -1169,15 +1181,12 @@ uint32_t NtClearEvent(uint32_t handle, uint32_t* previousState)
|
|||
return ResetEvent((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
uint32_t NtResumeThread(uint32_t hThread, uint32_t* suspendCount)
|
||||
uint32_t NtResumeThread(GuestThreadHandle* hThread, uint32_t* suspendCount)
|
||||
{
|
||||
DWORD count = ResumeThread((HANDLE)hThread);
|
||||
assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE));
|
||||
|
||||
if (count == (DWORD)-1)
|
||||
return E_FAIL;
|
||||
|
||||
if (suspendCount != nullptr)
|
||||
*suspendCount = ByteSwap(count);
|
||||
hThread->suspended = false;
|
||||
hThread->suspended.notify_all();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
@ -1270,9 +1279,9 @@ uint32_t ExCreateThread(XLPDWORD handle, uint32_t stackSize, XLPDWORD threadId,
|
|||
LOGF_UTILITY("0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}",
|
||||
(intptr_t)handle, stackSize, (intptr_t)threadId, xApiThreadStartup, startAddress, startContext, creationFlags);
|
||||
|
||||
DWORD hostThreadId;
|
||||
uint32_t hostThreadId;
|
||||
|
||||
*handle = (uint32_t)GuestThread::Start(startAddress, startContext, creationFlags, &hostThreadId);
|
||||
*handle = GetKernelHandle(GuestThread::Start({ startAddress, startContext, creationFlags }, &hostThreadId));
|
||||
|
||||
if (threadId != nullptr)
|
||||
*threadId = hostThreadId;
|
||||
|
|
@ -1391,10 +1400,13 @@ DWORD XAudioGetVoiceCategoryVolumeChangeMask(DWORD Driver, XLPDWORD Mask)
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t KeResumeThread(uint32_t object)
|
||||
uint32_t KeResumeThread(GuestThreadHandle* object)
|
||||
{
|
||||
LOGF_UTILITY("0x{:x}", object);
|
||||
return ResumeThread((HANDLE)object);
|
||||
assert(object != GetKernelObject(CURRENT_THREAD_HANDLE));
|
||||
|
||||
object->suspended = false;
|
||||
object->suspended.notify_all();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KeInitializeSemaphore(XKSEMAPHORE* semaphore, uint32_t count, uint32_t limit)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,11 @@ struct KernelObject
|
|||
{
|
||||
virtual ~KernelObject()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
virtual void Wait(uint32_t timeout)
|
||||
{
|
||||
assert(false && "Wait not implemented for this kernel object.");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
Video::StartPipelinePrecompilation();
|
||||
|
||||
GuestThread::Start(entry);
|
||||
GuestThread::Start({ entry, 0, 0 });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue