Core: Threadsafety Synchronization Fixes (Frame Advance / FifoPlayer)

Fix Frame Advance and FifoPlayer pause/unpause/stop.

CPU::EnableStepping is not atomic but is called from multiple threads
which races and leaves the system in a random state; also instruction
stepping was unstable, m_StepEvent had an almost random value because
of the dual purpose it served which could cause races where CPU::Run
would SingleStep when it was supposed to be sleeping.

FifoPlayer never FinishStateMove()d which was causing it to deadlock.
Rather than partially reimplementing CPU::Run, just use CPUCoreBase
and then call CPU::Run(). More DRY and less likely to have weird bugs
specific to the player (i.e the previous freezing on pause/stop).

Refactor PowerPC::state into CPU since it manages the state of the
CPU Thread which is controlled by CPU, not PowerPC. This simplifies
the architecture somewhat and eliminates races that can be caused by
calling PowerPC state functions directly instead of using CPU's
(because they bypassed the EnableStepping lock).
This commit is contained in:
EmptyChaos committed 2016-05-13 09:23:44 +10:00
1 parent 0283ce2a7c
commit c1922783f8
27 files changed
+590 -319

No files matched your search

+14 -16
View File
@@ -11,14 +11,12 @@
#include "Common/CommonTypes.h"
#include "Core/Debugger/PPCDebugInterface.h"
#include "Core/PowerPC/CPUCoreBase.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCCache.h"
class CPUCoreBase;
class PointerWrap;
extern CPUCoreBase *cpu_core_base;
namespace PowerPC
{
@@ -129,13 +127,6 @@ struct PowerPCState
static_assert(offsetof(PowerPC::PowerPCState, above_fits_in_first_0x100) <= 0x100, "top of PowerPCState too big");
#endif
enum CPUState
{
CPU_RUNNING = 0,
CPU_STEPPING = 2,
CPU_POWERDOWN = 3,
};
extern PowerPCState ppcState;
extern Watches watches;
@@ -148,19 +139,26 @@ void Shutdown();
void DoState(PointerWrap &p);
CoreMode GetMode();
// [NOT THREADSAFE] CPU Thread or CPU::PauseAndLock or CORE_UNINITIALIZED
void SetMode(CoreMode _coreType);
const char* GetCPUName();
// Set the current CPU Core to the given implementation until removed.
// Remove the current injected CPU Core by passing nullptr.
// While an external CPUCoreBase is injected, GetMode() will return MODE_INTERPRETER.
// Init() will be called when added and Shutdown() when removed.
// [Threadsafety: Same as SetMode(), except it cannot be called from inside the CPU
// run loop on the CPU Thread - it doesn't make sense for a CPU to remove itself
// while it is CPU_RUNNING]
void InjectExternalCPUCore(CPUCoreBase* core);
// Stepping requires the CPU Execution lock (CPU::PauseAndLock or CPU Thread)
// It's not threadsafe otherwise.
void SingleStep();
void CheckExceptions();
void CheckExternalExceptions();
void CheckBreakPoints();
void RunLoop();
void Start();
void Pause();
void Stop();
void FinishStateMove();
CPUState GetState();
const volatile CPUState *GetStatePtr(); // this oddity is here instead of an extern declaration to easily be able to find all direct accesses throughout the code.
u32 CompactCR();
void ExpandCR(u32 cr);