diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 5e3f11415f..2dd62b4d40 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -739,7 +739,7 @@ std::string cpu_thread::dump_misc() const return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load()); } -void cpu_thread::suspend_work::push(cpu_thread* _this) noexcept +bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspended) noexcept { // Can't allow pre-set wait bit (it'd be a problem) verify(HERE), !_this || !(_this->state & cpu_flag::wait); @@ -758,6 +758,12 @@ void cpu_thread::suspend_work::push(cpu_thread* _this) noexcept // Load current head next = queue.load(); + if (!next && cancel_if_not_suspended) [[unlikely]] + { + // Give up if not suspended + return false; + } + if (!_this && next) { // If _this == nullptr, it only works if this is the first workload pushed @@ -869,10 +875,11 @@ void cpu_thread::suspend_work::push(cpu_thread* _this) noexcept } _this->check_state(); - return; + return true; } g_suspend_counter.notify_all(); + return true; } void cpu_thread::stop_all() noexcept diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 04cf307c9c..11f62203fb 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -136,7 +136,7 @@ public: suspend_work* next; // Internal method - void push(cpu_thread* _this) noexcept; + bool push(cpu_thread* _this, bool cancel_if_not_suspended = false) noexcept; }; // Suspend all threads and execute op (may be executed by other thread than caller!) @@ -167,6 +167,21 @@ public: } } + // Push the workload only if threads are being suspended by suspend_all() + template + static bool if_suspended(cpu_thread* _this, F op) + { + static_assert(std::is_void_v>, "Unimplemented (must return void)"); + { + suspend_work work{Prio, &op, nullptr, [](void* func, void*) + { + std::invoke(*static_cast(func)); + }}; + + return work.push(_this, true); + } + } + // Stop all threads with cpu_flag::dbg_global_stop static void stop_all() noexcept; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 7c937a5342..f781f312eb 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1189,10 +1189,27 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) ppu.use_full_rdata = false; } - for (u64 count = 0;; [&]() + for (u64 count = 0; count != umax; [&]() { if (ppu.state) { + if (ppu.state & cpu_flag::pause) + { + verify(HERE), cpu_thread::if_suspended<-1>(&ppu, [&]() + { + // Guaranteed success + ppu.rtime = vm::reservation_acquire(addr, sizeof(T)) & -128; + mov_rdata(ppu.rdata, *vm::get_super_ptr(addr & -128)); + }); + + // Exit loop + if ((ppu.rtime & 127) == 0) + { + count = -1; + return; + } + } + ppu.check_state(); } else if (++count < 20) [[likely]] @@ -1275,6 +1292,10 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) return static_cast(rdata << data_off >> size_off); } } + + be_t rdata; + std::memcpy(&rdata, &ppu.rdata[addr & 0x78], 8); + return static_cast(rdata << data_off >> size_off); } extern u32 ppu_lwarx(ppu_thread& ppu, u32 addr) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index b6e8db1a19..576c85871d 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -2428,11 +2428,23 @@ bool spu_thread::process_mfc_cmd() mov_rdata(temp, rdata); } - for (u64 i = 0;; [&]() + for (u64 i = 0; i != umax; [&]() { if (state & cpu_flag::pause) { - check_state(); + verify(HERE), cpu_thread::if_suspended<-1>(this, [&] + { + // Guaranteed success + ntime = vm::reservation_acquire(addr, 128); + mov_rdata(rdata, *vm::get_super_ptr(addr)); + }); + + // Exit loop + if ((ntime & 127) == 0) + { + i = -1; + return; + } } if (++i < 25) [[likely]]