Merge pull request #2378 from ergo720/sb_fix
Fixed slowness in Steel Battalion caused by WaitApc
This commit is contained in:
commit
cfa7be71cf
|
@ -1003,10 +1003,16 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
LARGE_INTEGER NewTime;
|
||||
PLARGE_INTEGER Timeout;
|
||||
if (dwMilliseconds == INFINITE) {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
Timeout = nullptr;
|
||||
}
|
||||
else if (dwMilliseconds == 0) {
|
||||
Timeout = &NewTime;
|
||||
NewTime.QuadPart = 0;
|
||||
}
|
||||
else {
|
||||
Timeout = &NewTime;
|
||||
NewTime.QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
|
||||
}
|
||||
|
@ -1017,7 +1023,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<DWORD>(dwRet);
|
||||
}, &NewTime, bAlertable, UserMode);
|
||||
}, Timeout, bAlertable, UserMode);
|
||||
|
||||
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
|
||||
}
|
||||
|
|
|
@ -112,43 +112,75 @@ void RestoreInterruptMode(bool value);
|
|||
void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql);
|
||||
|
||||
template<typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PETHREAD eThread, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
{
|
||||
if (const auto ret = Lambda()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
xbox::KiApcListMtx.lock();
|
||||
bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]);
|
||||
xbox::KiApcListMtx.unlock();
|
||||
|
||||
if (EmptyKernel == false) {
|
||||
xbox::KiExecuteKernelApc();
|
||||
}
|
||||
|
||||
if ((EmptyUser == false) &&
|
||||
(Alertable == TRUE) &&
|
||||
(WaitMode == xbox::UserMode)) {
|
||||
xbox::KiExecuteUserApc();
|
||||
return X_STATUS_USER_APC;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER Timeout, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
{
|
||||
// NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
|
||||
// also interrupt the wait
|
||||
|
||||
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;
|
||||
}
|
||||
if (Timeout == nullptr) {
|
||||
// No timout specified, so this is an infinite wait until an alert, a user apc or the object(s) become(s) signalled
|
||||
while (true) {
|
||||
if (const auto ret = SatisfyWait(Lambda, eThread, Alertable, WaitMode)) {
|
||||
return *ret;
|
||||
}
|
||||
|
||||
while (now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
|
||||
if (const auto ret = Lambda()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
else if (Timeout->QuadPart == 0) {
|
||||
// A zero timeout means that we only have to check the conditions once and then return immediately if they are not satisfied
|
||||
if (const auto ret = SatisfyWait(Lambda, eThread, Alertable, WaitMode)) {
|
||||
return *ret;
|
||||
}
|
||||
|
||||
xbox::KiApcListMtx.lock();
|
||||
bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]);
|
||||
xbox::KiApcListMtx.unlock();
|
||||
if (EmptyKernel == false) {
|
||||
xbox::KiExecuteKernelApc();
|
||||
else {
|
||||
return X_STATUS_TIMEOUT;
|
||||
}
|
||||
if ((EmptyUser == false) &&
|
||||
(Alertable == TRUE) &&
|
||||
(WaitMode == xbox::UserMode)) {
|
||||
xbox::KiExecuteUserApc();
|
||||
return X_STATUS_USER_APC;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
now = xbox::KeQueryInterruptTime();
|
||||
}
|
||||
else {
|
||||
// A non-zero timeout means we have to check the conditions until we reach the requested time
|
||||
xbox::LARGE_INTEGER ExpireTime, DueTime, NewTime;
|
||||
xbox::ulonglong_xt Now;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart; // either positive, negative, but not NULL
|
||||
xbox::PLARGE_INTEGER AbsoluteExpireTime = xbox::KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime, &Now);
|
||||
while (Now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
|
||||
if (const auto ret = SatisfyWait(Lambda, eThread, Alertable, WaitMode)) {
|
||||
return *ret;
|
||||
}
|
||||
|
||||
return X_STATUS_TIMEOUT;
|
||||
std::this_thread::yield();
|
||||
Now = xbox::KeQueryInterruptTime();
|
||||
}
|
||||
|
||||
return X_STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -714,17 +714,6 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
|
|||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
// 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;
|
||||
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
pNewTime = &NewTime;
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
|
@ -735,7 +724,12 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
|
|||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, pNewTime, Alertable, WaitMode);
|
||||
}, Interval, Alertable, WaitMode);
|
||||
|
||||
if (ret == X_STATUS_TIMEOUT) {
|
||||
// NOTE: this function considers a timeout a success
|
||||
ret = X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
|
|
@ -929,6 +929,25 @@ xbox::void_xt xbox::KiExecuteUserApc()
|
|||
KiExecuteApc<UserMode>();
|
||||
}
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
|
||||
(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
IN xbox::PLARGE_INTEGER DueTime,
|
||||
IN OUT xbox::PLARGE_INTEGER NewTime,
|
||||
OUT ulonglong_xt *Now
|
||||
)
|
||||
{
|
||||
*Now = xbox::KeQueryInterruptTime();
|
||||
if (OriginalTime->QuadPart >= 0) {
|
||||
return OriginalTime;
|
||||
}
|
||||
else {
|
||||
NewTime->QuadPart = *Now;
|
||||
NewTime->QuadPart -= DueTime->QuadPart;
|
||||
return NewTime;
|
||||
}
|
||||
}
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
|
||||
(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
|
|
|
@ -140,6 +140,14 @@ namespace xbox
|
|||
void_xt KiExecuteKernelApc();
|
||||
void_xt KiExecuteUserApc();
|
||||
|
||||
PLARGE_INTEGER FASTCALL KiComputeWaitInterval
|
||||
(
|
||||
IN PLARGE_INTEGER OriginalTime,
|
||||
IN PLARGE_INTEGER DueTime,
|
||||
IN OUT PLARGE_INTEGER NewTime,
|
||||
OUT ulonglong_xt *Now
|
||||
);
|
||||
|
||||
PLARGE_INTEGER FASTCALL KiComputeWaitInterval
|
||||
(
|
||||
IN PLARGE_INTEGER OriginalTime,
|
||||
|
|
|
@ -2208,17 +2208,6 @@ 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;
|
||||
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
pNewTime = &NewTime;
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Count, &nativeHandles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
|
@ -2233,7 +2222,7 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
|
|||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, pNewTime, Alertable, WaitMode);
|
||||
}, Timeout, Alertable, WaitMode);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue