Fixed a bug in NtWaitForMultipleObjectsEx that caused the dashboard to deadlock + more review remarks

This commit is contained in:
ergo720 2022-02-17 11:43:06 +01:00
parent 7589f0a94c
commit 6867907a3c
6 changed files with 33 additions and 17 deletions

View File

@ -998,9 +998,18 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
LOG_FUNC_ARG(bAlertable)
LOG_FUNC_END;
LARGE_INTEGER ExpireTime, DueTime, NewTime;
ExpireTime.QuadPart = DueTime.QuadPart = (dwMilliseconds == INFINITE) ? ~0ull : -dwMilliseconds;
KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
LARGE_INTEGER NewTime;
PLARGE_INTEGER pNewTime;
if (dwMilliseconds == INFINITE) {
NewTime.QuadPart = ~0ull;
pNewTime = &NewTime;
}
else {
LARGE_INTEGER ExpireTime, DueTime;
ExpireTime.QuadPart = DueTime.QuadPart = -dwMilliseconds;
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
}
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
@ -1008,7 +1017,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
return std::nullopt;
}
return std::make_optional<DWORD>(dwRet);
}, &NewTime, bAlertable, UserMode);
}, pNewTime, bAlertable, UserMode);
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
}

View File

@ -115,6 +115,11 @@ xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, x
xbox::ulonglong_xt now = xbox::KeQueryInterruptTime();
xbox::PETHREAD eThread = reinterpret_cast<xbox::PETHREAD>(EmuKeGetPcr()->Prcb->CurrentThread);
if (AbsoluteExpireTime->QuadPart == 0) {
// This will only happen when the title specifies a zero timeout
AbsoluteExpireTime->QuadPart = now;
}
while (now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
if (const auto ret = Lambda()) {
return *ret;

View File

@ -567,13 +567,15 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
// We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well
// Test case: Metal Slug 3
LARGE_INTEGER NewTime;
PLARGE_INTEGER pNewTime;
if (Interval) {
LARGE_INTEGER ExpireTime, DueTime;
ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart;
KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
}
else {
NewTime.QuadPart = ~0ull;
pNewTime = &NewTime;
}
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
@ -584,7 +586,7 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
return std::nullopt;
}
return std::make_optional<ntstatus_xt>(Status);
}, &NewTime, Alertable, WaitMode);
}, pNewTime, Alertable, WaitMode);
RETURN(ret);
}

View File

@ -2201,13 +2201,15 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
LARGE_INTEGER NewTime;
PLARGE_INTEGER pNewTime;
if (Timeout) {
LARGE_INTEGER ExpireTime, DueTime;
ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart;
KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
}
else {
NewTime.QuadPart = ~0ull;
pNewTime = &NewTime;
}
xbox::ntstatus_xt ret = WaitApc([Count, Handles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
@ -2223,7 +2225,7 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
return std::nullopt;
}
return std::make_optional<ntstatus_xt>(Status);
}, &NewTime, Alertable, WaitMode);
}, pNewTime, Alertable, WaitMode);
RETURN(ret);
}

View File

@ -62,9 +62,9 @@ typedef struct _PCSTProxyParam
}
PCSTProxyParam;
xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr };
int g_iThreadNotificationCount = 0;
static std::mutex PspThreadNotificationMtx;
static xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr };
static std::atomic_int g_iThreadNotificationCount = 0;
static std::mutex g_ThreadNotificationMtx;
// Separate function for logging, otherwise in PCSTProxy __try wont work (Compiler Error C2712)
void LOG_PCSTProxy
@ -142,7 +142,7 @@ xbox::PETHREAD xbox::PspGetCurrentThread()
static xbox::void_xt PspCallThreadNotificationRoutines(xbox::PETHREAD eThread, xbox::boolean_xt Create)
{
std::unique_lock lck(PspThreadNotificationMtx);
std::unique_lock lck(g_ThreadNotificationMtx);
for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
if (g_pfnThreadNotification[i]) {
EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]);
@ -251,7 +251,6 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
RETURN(result);
}
// Call the thread notification routine(s)
if (g_iThreadNotificationCount) {
PspCallThreadNotificationRoutines(eThread, TRUE);
}
@ -269,7 +268,6 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
// PCSTProxy is responsible for cleaning up this pointer
PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam;
iPCSTProxyParam->StartRoutine = (PVOID)StartRoutine;
iPCSTProxyParam->StartContext = StartContext;
iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown?
@ -344,6 +342,7 @@ XBSYSAPI EXPORTNUM(257) xbox::ntstatus_xt NTAPI xbox::PsSetCreateThreadNotifyRou
{
LOG_FUNC_ONE_ARG(NotifyRoutine);
std::unique_lock lck(g_ThreadNotificationMtx);
for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
if (g_pfnThreadNotification[i] == zeroptr) {
g_pfnThreadNotification[i] = NotifyRoutine;
@ -368,7 +367,6 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
{
LOG_FUNC_ONE_ARG(ExitStatus);
// Call the thread notification routine(s)
xbox::PETHREAD eThread = xbox::PspGetCurrentThread();
if (eThread->UniqueThread && g_iThreadNotificationCount) {
PspCallThreadNotificationRoutines(eThread, FALSE);

View File

@ -41,7 +41,7 @@ std::shared_mutex g_MapMtx;
void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle)
{
std::unique_lock<std::shared_mutex> lck(g_MapMtx);
const auto &ret = g_RegisteredHandles.try_emplace(xhandle, nhandle);
auto ret = g_RegisteredHandles.try_emplace(xhandle, nhandle);
if (ret.second == false) {
// This can happen when an ob handle has been destroyed, but then a thread switch happens before the first thread
// got a chance to remove the old handle from g_RegisteredHandles with RemoveXboxHandle
@ -52,7 +52,7 @@ void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle)
lck.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(5));
lck.lock();
g_RegisteredHandles.try_emplace(xhandle, nhandle);
ret = g_RegisteredHandles.try_emplace(xhandle, nhandle);
if (ret.second) {
return;
}