Android VM lifecycle: prompt EE stop + fix 2nd-launch kick-back

- R5900 _cpuEventTest_Shared: bail out early on VMState::Stopping/Shutdown and
  on IsExecutionInterrupted() so the EE leaves Execute() promptly on stop
  (previously hit the ~5s shutdown timeout).
- native-lib Host::RequestVMShutdown: no-op if already Shutdown. The queued
  task could re-set Stopping after the run loop already reached Shutdown,
  sticking s_state at Stopping so the next Initialize failed 'already running'
  -- kick back to library on the 2nd game boot.
This commit is contained in:
jpolo1224
2026-07-09 10:23:58 -04:00
parent 1a62f62d2a
commit 2512027e9a
2 changed files with 34 additions and 0 deletions
+25
View File
@@ -363,6 +363,20 @@ __fi void _cpuEventTest_Shared()
eeEventTestIsActive = true;
cpuRegs.nextEventCycle = cpuRegs.cycle + eeWaitCycles;
cpuRegs.lastEventCycle = cpuRegs.cycle;
// Android's in-game Exit/Reset can flip the VM to Stopping from another
// thread while the EE recompiler is in its generated-code event path. Once
// Stopping is visible, do not continue into IOP counters/VU sync; return
// immediately so the recompiler's outer recEventTest can fastjmp out of
// Cpu->Execute(). Without this, PSX-vsync/IOP work can keep running after
// the stop latch and the Java shutdown call times out.
const VMState vm_state = VMManager::GetState();
if (vm_state == VMState::Stopping || vm_state == VMState::Shutdown)
{
eeEventTestIsActive = false;
return;
}
// ---- INTC / DMAC (CPU-level Exceptions) -----------------
// Done first because exceptions raised during event tests need to be postponed a few
// cycles (fixes Grandia II [PAL], which does a spin loop on a vsync and expects to
@@ -427,6 +441,17 @@ __fi void _cpuEventTest_Shared()
_cpuTestInterrupts();
}
#if defined(__ANDROID__)
// Android pause/stop requests can be pumped during the counter/vsync work
// above. Once that happens, return to the rec/interpreter wrapper immediately
// instead of continuing through VU sync and scheduling a fresh event.
if (VMManager::Internal::IsExecutionInterrupted())
{
eeEventTestIsActive = false;
return;
}
#endif
// ---- VU Sync -------------
// We're in a EventTest. All dynarec registers are flushed
// so there is no need to freeze registers here.
@@ -2598,6 +2598,15 @@ void Host::RequestExitBigPicture()
void Host::RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state)
{
// This runs as a queued CPU-thread task (Host::RunOnCPUThread from the shutdown
// JNI). Since the EE now bails out of Execute() promptly on Stopping, the run
// loop can reach its post-Shutdown message pump and process THIS task AFTER
// VMManager::Shutdown(false) already drove s_state to Shutdown. Re-setting
// Stopping here would leave s_state stuck at Stopping, so the next game's
// VMManager::Initialize fails with "already running" (kick-back to library on
// the 2nd launch). If we're already shut down, there's nothing left to stop.
if (VMManager::GetState() == VMState::Shutdown)
return;
VMManager::SetState(VMState::Stopping);
if (!s_execute_exit.load(std::memory_order_acquire) && Cpu)
Cpu->ExitExecution();