diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 4d51e8ba7..f63635e2a 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -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 dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable); @@ -1008,7 +1017,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait) return std::nullopt; } return std::make_optional(dwRet); - }, &NewTime, bAlertable, UserMode); + }, pNewTime, bAlertable, UserMode); RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret); } diff --git a/src/core/kernel/exports/EmuKrnl.h b/src/core/kernel/exports/EmuKrnl.h index 5414174b8..5b3247eeb 100644 --- a/src/core/kernel/exports/EmuKrnl.h +++ b/src/core/kernel/exports/EmuKrnl.h @@ -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(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(AbsoluteExpireTime->QuadPart)) { if (const auto ret = Lambda()) { return *ret; diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 82c9d306a..1d27e5051 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -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 { @@ -584,7 +586,7 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread return std::nullopt; } return std::make_optional(Status); - }, &NewTime, Alertable, WaitMode); + }, pNewTime, Alertable, WaitMode); RETURN(ret); } diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index e62ef2ad5..02935c80f 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -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 { @@ -2223,7 +2225,7 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx return std::nullopt; } return std::make_optional(Status); - }, &NewTime, Alertable, WaitMode); + }, pNewTime, Alertable, WaitMode); RETURN(ret); } diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index ecf57fc0c..0b569a2f2 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -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); diff --git a/src/core/kernel/support/NativeHandle.cpp b/src/core/kernel/support/NativeHandle.cpp index 74f95141f..9b745d818 100644 --- a/src/core/kernel/support/NativeHandle.cpp +++ b/src/core/kernel/support/NativeHandle.cpp @@ -41,7 +41,7 @@ std::shared_mutex g_MapMtx; void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle) { std::unique_lock 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; }