Avoid using std::async in WaitApc
This commit is contained in:
parent
a769e896c6
commit
7589f0a94c
|
@ -38,6 +38,7 @@
|
|||
#include "Logging.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\kernel\exports\EmuKrnl.h" // For DefaultLaunchDataPage
|
||||
#include "core\kernel\exports\EmuKrnlKi.h"
|
||||
#include "core\kernel\support\EmuFile.h"
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "EmuShared.h"
|
||||
|
@ -997,16 +998,19 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
LOG_FUNC_ARG(bAlertable)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
bool Exit = false;
|
||||
auto async_bool = WaitApc(bAlertable, UserMode, &Exit);
|
||||
LARGE_INTEGER ExpireTime, DueTime, NewTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = (dwMilliseconds == INFINITE) ? ~0ull : -dwMilliseconds;
|
||||
KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
|
||||
dword_xt dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable);
|
||||
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
|
||||
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
|
||||
if (dwRet == WAIT_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<DWORD>(dwRet);
|
||||
}, &NewTime, bAlertable, UserMode);
|
||||
|
||||
Exit = true;
|
||||
bool result = async_bool.get();
|
||||
|
||||
RETURN(result ? X_STATUS_USER_APC : dwRet);
|
||||
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -105,6 +105,7 @@ typedef void* LPSECURITY_ATTRIBUTES;
|
|||
#define X_STATUS_NOT_COMMITTED 0xC000002DL
|
||||
#define X_STATUS_UNRECOGNIZED_VOLUME 0xC000014FL
|
||||
#define X_STATUS_OBJECT_PATH_NOT_FOUND 0xC000003AL
|
||||
#define X_STATUS_TIMEOUT 0x00000102L
|
||||
|
||||
// ******************************************************************
|
||||
// * Registry value types
|
||||
|
|
|
@ -210,50 +210,6 @@ const DWORD IrqlMasks[] = {
|
|||
0x00000000, // IRQL 31 (HIGH_LEVEL)
|
||||
};
|
||||
|
||||
// This helper function is used to signal WinApi waiting functions that the wait has been satisfied by an xbox user APC
|
||||
static void WINAPI EndWait(ULONG_PTR Parameter)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
std::future<bool> WaitApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bool *Exit)
|
||||
{
|
||||
// NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
|
||||
// also interrupt the wait
|
||||
xbox::PKPCR Kpcr = EmuKeGetPcr();
|
||||
|
||||
// This new thread must execute APCs in the context of the calling thread
|
||||
return std::async(std::launch::async, [Kpcr, Alertable, WaitMode, Exit]() {
|
||||
EmuKeSetPcr(Kpcr);
|
||||
xbox::PETHREAD eThread = reinterpret_cast<xbox::PETHREAD>(Kpcr->Prcb->CurrentThread);
|
||||
|
||||
while (true) {
|
||||
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();
|
||||
// Queue a native APC to the calling thread to forcefully terminate the wait of the WinApi functions,
|
||||
// in the case it didn't terminate already
|
||||
[[maybe_unused]] BOOL ret = QueueUserAPC(EndWait, *GetNativeHandle(eThread->UniqueThread), 0);
|
||||
assert(ret);
|
||||
EmuKeSetPcr(nullptr);
|
||||
return true;
|
||||
}
|
||||
SwitchToThread();
|
||||
if (*Exit) {
|
||||
EmuKeSetPcr(nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x0033 - InterlockedCompareExchange()
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include <future>
|
||||
|
||||
// CONTAINING_RECORD macro
|
||||
|
@ -104,6 +105,40 @@ extern HalSystemInterrupt HalSystemInterrupts[MAX_BUS_INTERRUPT_LEVEL + 1];
|
|||
bool DisableInterrupts();
|
||||
void RestoreInterruptMode(bool value);
|
||||
void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql);
|
||||
std::future<bool> WaitApc(xbox::boolean_xt Alertable, xbox::char_xt WaitMode, bool *Exit);
|
||||
|
||||
template<typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, 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);
|
||||
|
||||
while (now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
now = xbox::KeQueryInterruptTime();
|
||||
}
|
||||
|
||||
return X_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -566,14 +566,27 @@ 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
|
||||
bool Exit = false;
|
||||
auto async_bool = WaitApc(Alertable, WaitMode, &Exit);
|
||||
LARGE_INTEGER NewTime;
|
||||
if (Interval) {
|
||||
LARGE_INTEGER ExpireTime, DueTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart;
|
||||
KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
}
|
||||
|
||||
NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER *)Interval);
|
||||
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
|
||||
if (Status == 0) { // STATUS_SUCCESS
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, &NewTime, Alertable, WaitMode);
|
||||
|
||||
Exit = true;
|
||||
bool result = async_bool.get();
|
||||
RETURN(result ? X_STATUS_USER_APC : ret);
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2000,24 +2013,6 @@ XBSYSAPI EXPORTNUM(156) xbox::dword_xt VOLATILE xbox::KeTickCount = 0;
|
|||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(157) xbox::ulong_xt xbox::KeTimeIncrement = CLOCK_TIME_INCREMENT;
|
||||
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL KiComputeWaitInterval(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
IN xbox::PLARGE_INTEGER DueTime,
|
||||
IN OUT xbox::PLARGE_INTEGER NewTime
|
||||
)
|
||||
{
|
||||
if (OriginalTime->QuadPart >= 0) {
|
||||
return OriginalTime;
|
||||
|
||||
}
|
||||
else {
|
||||
NewTime->QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime->QuadPart -= DueTime->QuadPart;
|
||||
return NewTime;
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x009E - KeWaitForMultipleObjects()
|
||||
// ******************************************************************
|
||||
|
|
|
@ -928,3 +928,20 @@ 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
|
||||
)
|
||||
{
|
||||
if (OriginalTime->QuadPart >= 0) {
|
||||
return OriginalTime;
|
||||
}
|
||||
else {
|
||||
NewTime->QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime->QuadPart -= DueTime->QuadPart;
|
||||
return NewTime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,13 @@ 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
|
||||
);
|
||||
};
|
||||
|
||||
extern xbox::KPROCESS KiUniqueProcess;
|
||||
|
|
|
@ -2200,19 +2200,32 @@ 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
|
||||
bool Exit = false;
|
||||
auto async_bool = WaitApc(Alertable, WaitMode, &Exit);
|
||||
LARGE_INTEGER NewTime;
|
||||
if (Timeout) {
|
||||
LARGE_INTEGER ExpireTime, DueTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart;
|
||||
KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
}
|
||||
|
||||
NTSTATUS ret = NtDll::NtWaitForMultipleObjects(
|
||||
Count,
|
||||
Handles,
|
||||
(NtDll::OBJECT_WAIT_TYPE)WaitType,
|
||||
Alertable,
|
||||
(NtDll::PLARGE_INTEGER)Timeout);
|
||||
xbox::ntstatus_xt ret = WaitApc([Count, Handles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
|
||||
Count,
|
||||
Handles,
|
||||
(NtDll::OBJECT_WAIT_TYPE)WaitType,
|
||||
Alertable,
|
||||
&ExpireTime);
|
||||
if (Status == STATUS_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, &NewTime, Alertable, WaitMode);
|
||||
|
||||
Exit = true;
|
||||
bool result = async_bool.get();
|
||||
RETURN(result ? X_STATUS_USER_APC : ret);
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -56,6 +56,7 @@ void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle)
|
|||
if (ret.second) {
|
||||
return;
|
||||
}
|
||||
now += std::chrono::milliseconds(5);
|
||||
}
|
||||
|
||||
// If we reach here, it means that we could not insert the handle after more than two seconds of trying. This probably means
|
||||
|
|
Loading…
Reference in New Issue