avoid deadlock when sh4 cpu is restarted while emu is being stopped

The sh4 cpu is stopped/restarted during a soft reset, and dynarec code
reset (arm64, arm, x86). If the emulator is stopped concurrently, the
call may hang. Use a mutex-protected method to restart the cpu only if
the emu is still running.
This commit is contained in:
Flyinghead 2024-10-12 17:31:58 +02:00
parent 6d2ed417be
commit 9793e54646
5 changed files with 31 additions and 10 deletions

View File

@ -688,7 +688,8 @@ void Emulator::runInternal()
{
nvmem::saveFiles();
dc_reset(false);
sh4_cpu.Start();
if (!restartCpu())
resetRequested = false;
}
} while (resetRequested);
}
@ -750,9 +751,12 @@ void Emulator::stop()
// Avoid race condition with GGPO restarting the sh4 for a new frame
if (config::GGPOEnable)
NetworkHandshake::term();
// must be updated after GGPO is stopped since it may run some rollback frames
state = Loaded;
sh4_cpu.Stop();
{
const std::lock_guard<std::mutex> _(mutex);
// must be updated after GGPO is stopped since it may run some rollback frames
state = Loaded;
sh4_cpu.Stop();
}
if (config::ThreadedRendering)
{
rend_cancel_emu_wait();
@ -1039,4 +1043,13 @@ void Emulator::vblank()
sh4_cpu.Stop();
}
bool Emulator::restartCpu()
{
const std::lock_guard<std::mutex> _(mutex);
if (state != Running)
return false;
sh4_cpu.Start();
return true;
}
Emulator emu;

View File

@ -165,6 +165,11 @@ public:
* Called internally on vblank.
*/
void vblank();
/**
* Restart the cpu iff the emu is still running
* Returns true if the cpu was started
*/
bool restartCpu();
private:
bool checkStatus(bool wait = false);

View File

@ -50,6 +50,7 @@ using namespace vixl::aarch32;
#include "cfg/option.h"
#include "arm_unwind.h"
#include "oslib/virtmem.h"
#include "emulator.h"
//#define CANONICALTEST
@ -428,8 +429,8 @@ public:
generate_mainloop();
::mainloop(context);
if (restarting)
p_sh4rcb->cntx.CpuRunning = 1;
if (restarting && !emu.restartCpu())
restarting = false;
} while (restarting);
}

View File

@ -43,6 +43,7 @@ using namespace vixl::aarch64;
#include "arm64_regalloc.h"
#include "hw/mem/addrspace.h"
#include "oslib/virtmem.h"
#include "emulator.h"
#undef do_sqw_nommu
@ -2213,8 +2214,8 @@ public:
generate_mainloop();
::mainloop(v_cntx);
if (restarting)
p_sh4rcb->cntx.CpuRunning = 1;
if (restarting && !emu.restartCpu())
restarting = false;
} while (restarting);
} catch (const SH4ThrownException&) {
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop");

View File

@ -27,6 +27,7 @@
#include "hw/sh4/sh4_mem.h"
#include "hw/mem/addrspace.h"
#include "oslib/unwind_info.h"
#include "emulator.h"
static void (*mainloop)();
static void (*ngen_FailedToFindBlock_)();
@ -900,8 +901,8 @@ public:
generate_mainloop();
::mainloop();
if (restarting)
p_sh4rcb->cntx.CpuRunning = 1;
if (restarting && !emu.restartCpu())
restarting = false;
} while (restarting);
} catch (const SH4ThrownException& e) {
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop %x pc %x", e.expEvn, e.epc);