HLE: Make HLE table access thread-safe

Previous code could sometimes resize the vector while a read access was happening
This commit is contained in:
Exzap 2025-06-22 20:56:47 +02:00
parent 522b5ef260
commit 5a4731f919
2 changed files with 32 additions and 24 deletions

View file

@ -2,62 +2,70 @@
#include "PPCInterpreterInternal.h" #include "PPCInterpreterInternal.h"
#include "PPCInterpreterHelper.h" #include "PPCInterpreterHelper.h"
std::unordered_set<std::string> sUnsupportedHLECalls; std::unordered_set<std::string> s_unsupportedHLECalls;
void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU) void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU)
{ {
const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8); const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8);
std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName); std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName);
if (sUnsupportedHLECalls.find(tempString) == sUnsupportedHLECalls.end()) if (s_unsupportedHLECalls.find(tempString) == s_unsupportedHLECalls.end())
{ {
cemuLog_log(LogType::UnsupportedAPI, "{}", tempString); cemuLog_log(LogType::UnsupportedAPI, "{}", tempString);
sUnsupportedHLECalls.emplace(tempString); s_unsupportedHLECalls.emplace(tempString);
} }
hCPU->gpr[3] = 0; hCPU->gpr[3] = 0;
PPCInterpreter_nextInstruction(hCPU); PPCInterpreter_nextInstruction(hCPU);
} }
std::vector<void(*)(PPCInterpreter_t* hCPU)>* sPPCHLETable{}; static constexpr size_t HLE_TABLE_CAPACITY = 0x4000;
HLECALL s_ppcHleTable[HLE_TABLE_CAPACITY]{};
sint32 s_ppcHleTableWriteIndex = 0;
std::mutex s_ppcHleTableMutex;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName) HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName)
{ {
if (!sPPCHLETable) std::unique_lock _l(s_ppcHleTableMutex);
sPPCHLETable = new std::vector<void(*)(PPCInterpreter_t* hCPU)>(); if (s_ppcHleTableWriteIndex >= HLE_TABLE_CAPACITY)
for (sint32 i = 0; i < sPPCHLETable->size(); i++)
{ {
if ((*sPPCHLETable)[i] == hleCall) cemuLog_log(LogType::Force, "HLE table is full");
return i; cemu_assert(false);
} }
HLEIDX newFuncIndex = (sint32)sPPCHLETable->size(); for (sint32 i = 0; i < s_ppcHleTableWriteIndex; i++)
sPPCHLETable->resize(sPPCHLETable->size() + 1); {
(*sPPCHLETable)[newFuncIndex] = hleCall; if (s_ppcHleTable[i] == hleCall)
return newFuncIndex; {
return i;
}
}
cemu_assert(s_ppcHleTableWriteIndex < HLE_TABLE_CAPACITY);
s_ppcHleTable[s_ppcHleTableWriteIndex] = hleCall;
HLEIDX funcIndex = s_ppcHleTableWriteIndex;
s_ppcHleTableWriteIndex++;
return funcIndex;
} }
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex) HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex)
{ {
if (funcIndex < 0 || funcIndex >= sPPCHLETable->size()) if (funcIndex < 0 || funcIndex >= HLE_TABLE_CAPACITY)
return nullptr; return nullptr;
return sPPCHLETable->data()[funcIndex]; return s_ppcHleTable[funcIndex];
} }
std::mutex g_hleLogMutex; std::mutex s_hleLogMutex;
void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode) void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode)
{ {
uint32 hleFuncId = opcode & 0xFFFF; uint32 hleFuncId = opcode & 0xFFFF;
if (hleFuncId == 0xFFD0) if (hleFuncId == 0xFFD0) [[unlikely]]
{ {
g_hleLogMutex.lock(); s_hleLogMutex.lock();
PPCInterpreter_handleUnsupportedHLECall(hCPU); PPCInterpreter_handleUnsupportedHLECall(hCPU);
g_hleLogMutex.unlock(); s_hleLogMutex.unlock();
return;
} }
else else
{ {
// os lib function // os lib function
cemu_assert(hleFuncId < sPPCHLETable->size()); auto hleCall = PPCInterpreter_getHLECall(hleFuncId);
auto hleCall = (*sPPCHLETable)[hleFuncId];
cemu_assert(hleCall); cemu_assert(hleCall);
hleCall(hCPU); hleCall(hCPU);
} }

View file

@ -230,9 +230,9 @@ static inline float flushDenormalToZero(float f)
// HLE interface // HLE interface
typedef void(*HLECALL)(PPCInterpreter_t* hCPU); using HLECALL = void(*)(PPCInterpreter_t*);
using HLEIDX = sint32;
typedef sint32 HLEIDX;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName); HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName);
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex); HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);