From c11074a128e94591e6bb58e763cbad83659719f7 Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 19 Feb 2020 22:25:53 +0200 Subject: [PATCH] RawSPU: fix race condition in RunCntl stop request --- rpcs3/Emu/Cell/RawSPUThread.cpp | 22 ++++++++++++++++++++-- rpcs3/Emu/Cell/SPUThread.cpp | 13 ++++++++++--- rpcs3/Emu/Cell/SPUThread.h | 1 + 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index cfd3833c95..93b74fc587 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -8,6 +8,8 @@ inline void try_start(spu_thread& spu) { + std::shared_lock lock(spu.run_ctrl_mtx); + if (spu.status_npc.fetch_op([](typename spu_thread::status_npc_sync_var& value) { if (value.status & SPU_STATUS_RUNNING) @@ -246,8 +248,24 @@ bool spu_thread::write_reg(const u32 addr, const u32 value) } else if (value == SPU_RUNCNTL_STOP_REQUEST) { - // TODO: Wait for the SPU to stop? - state += cpu_flag::stop; + if (get_current_cpu_thread() == this) + { + // TODO + state += cpu_flag::stop; + return true; + } + + std::scoped_lock lock(run_ctrl_mtx); + + if (status_npc.load().status & SPU_STATUS_RUNNING) + { + state += cpu_flag::stop; + + for (status_npc_sync_var old; (old = status_npc).status & SPU_STATUS_RUNNING;) + { + status_npc.wait(old); + } + } } else { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index fdeb7d9c87..aade51f819 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1096,19 +1096,22 @@ void spu_thread::cpu_stop() { if (!group && offset >= RAW_SPU_BASE_ADDR) { - status_npc.fetch_op([this](status_npc_sync_var& state) + if (status_npc.fetch_op([this](status_npc_sync_var& state) { if (state.status & SPU_STATUS_RUNNING) { // Save next PC and current SPU Interrupt Status // Used only by RunCtrl stop requests state.status &= ~SPU_STATUS_RUNNING; - state.npc = pc | +interrupts_enabled; + state.npc = pc | +interrupts_enabled; return true; } return false; - }); + }).second) + { + status_npc.notify_one(); + } } else if (group && is_stopped()) { @@ -2842,6 +2845,8 @@ bool spu_thread::stop_and_signal(u32 code) state.npc = (pc + 4) | +interrupts_enabled; }); + status_npc.notify_one(); + int_ctrl[2].set(SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT); check_state(); return true; @@ -3196,6 +3201,8 @@ void spu_thread::halt() state.npc = pc | +interrupts_enabled; }); + status_npc.notify_one(); + int_ctrl[2].set(SPU_INT2_STAT_SPU_HALT_OR_STEP_INT); spu_runtime::g_escape(this); diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index b302dc71a6..a4f28230eb 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -580,6 +580,7 @@ public: u32 ch_dec_value; // written decrementer value atomic_t run_ctrl; // SPU Run Control register (only provided to get latest data written) + shared_mutex run_ctrl_mtx; struct alignas(8) status_npc_sync_var {