Make sure to reset WaitStatus when a new wait starts

This fixes an issue in Panzer Dragoon Orta, where KeDelayExecutionThread would return X_STATUS_TIMEOUT | X_STATUS_USER_APC
This commit is contained in:
ergo720 2023-03-22 21:42:34 +01:00
parent 680340f53d
commit ac31523b09
3 changed files with 13 additions and 9 deletions

View File

@ -960,9 +960,10 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
}
PKTHREAD kThread = KeGetCurrentThread();
kThread->WaitStatus = X_STATUS_SUCCESS;
if (!Timeout || (Timeout->QuadPart == 0)) {
// Use the built-in ktimer as a dummy wait object, so that KiUnwaitThreadAndLock can still work
PKTHREAD kThread = KeGetCurrentThread();
xbox::PKWAIT_BLOCK WaitBlock = &kThread->TimerWaitBlock;
kThread->WaitBlockList = WaitBlock;
xbox::PKTIMER Timer = &kThread->Timer;
@ -971,7 +972,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
Timer->Header.WaitListHead.Blink = &WaitBlock->WaitListEntry;
}
xbox::ntstatus_xt status = WaitApc<true>([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
xbox::ntstatus_xt status = WaitApc<true>([hObjectToSignal, hObjectToWaitOn, bAlertable, kThread]() -> std::optional<DWORD> {
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
if (dwRet == WAIT_TIMEOUT) {
return std::nullopt;
@ -986,7 +987,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
case WAIT_OBJECT_0: Status = X_STATUS_SUCCESS; break;
default: Status = X_STATUS_INVALID_HANDLE;
}
xbox::KiUnwaitThreadAndLock(xbox::KeGetCurrentThread(), Status, 0);
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
return std::make_optional<ntstatus_xt>(Status);
}, Timeout, bAlertable, UserMode);

View File

@ -733,9 +733,10 @@ 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
PKTHREAD kThread = KeGetCurrentThread();
kThread->WaitStatus = X_STATUS_SUCCESS;
if (!Interval || (Interval->QuadPart == 0)) {
// Use the built-in ktimer as a dummy wait object, so that KiUnwaitThreadAndLock can still work
PKTHREAD kThread = KeGetCurrentThread();
xbox::PKWAIT_BLOCK WaitBlock = &kThread->TimerWaitBlock;
kThread->WaitBlockList = WaitBlock;
xbox::PKTIMER Timer = &kThread->Timer;
@ -744,7 +745,7 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
Timer->Header.WaitListHead.Blink = &WaitBlock->WaitListEntry;
}
xbox::ntstatus_xt ret = WaitApc<true>([Alertable]() -> std::optional<ntstatus_xt> {
xbox::ntstatus_xt ret = WaitApc<true>([Alertable, kThread]() -> std::optional<ntstatus_xt> {
NtDll::LARGE_INTEGER ExpireTime;
ExpireTime.QuadPart = 0;
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
@ -752,9 +753,10 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
if (Status >= 0 && Status != STATUS_ALERTED && Status != STATUS_USER_APC) {
return std::nullopt;
}
EmuLog(LOG_LEVEL::DEBUG, "KeDelayExecutionThread -> Staus: %X", Status);
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
// to the thread. Test case: Steel Battalion
xbox::KiUnwaitThreadAndLock(xbox::KeGetCurrentThread(), Status, 0);
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
return std::make_optional<ntstatus_xt>(Status);
}, Interval, Alertable, WaitMode);

View File

@ -2231,9 +2231,10 @@ 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
PKTHREAD kThread = KeGetCurrentThread();
kThread->WaitStatus = X_STATUS_SUCCESS;
if (!Timeout || (Timeout->QuadPart == 0)) {
// Use the built-in ktimer as a dummy wait object, so that KiUnwaitThreadAndLock can still work
PKTHREAD kThread = KeGetCurrentThread();
xbox::PKWAIT_BLOCK WaitBlock = &kThread->TimerWaitBlock;
kThread->WaitBlockList = WaitBlock;
xbox::PKTIMER Timer = &kThread->Timer;
@ -2242,7 +2243,7 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
Timer->Header.WaitListHead.Blink = &WaitBlock->WaitListEntry;
}
xbox::ntstatus_xt ret = WaitApc<true>([Count, &nativeHandles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
xbox::ntstatus_xt ret = WaitApc<true>([Count, &nativeHandles, WaitType, Alertable, kThread]() -> std::optional<ntstatus_xt> {
NtDll::LARGE_INTEGER ExpireTime;
ExpireTime.QuadPart = 0;
NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
@ -2256,7 +2257,7 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
}
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
// to the thread. Test case: Steel Battalion
xbox::KiUnwaitThreadAndLock(xbox::KeGetCurrentThread(), Status, 0);
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
return std::make_optional<ntstatus_xt>(Status);
}, Timeout, Alertable, WaitMode);