diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 9494252b0cc5..ba45ae39ac2b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1285,7 +1285,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, T reg_value) } } - if (res == ppu.rtime && vm::reservation_trylock(res, ppu.rtime)) + if (res == ppu.rtime && vm::reservation_trylock(res, ppu.rtime).first) { if (data.compare_and_swap_test(old_data, reg_value)) { @@ -1300,7 +1300,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, T reg_value) return false; } - if (!vm::reservation_trylock(res, ppu.rtime)) + if (!vm::reservation_trylock(res, ppu.rtime).first) { return false; } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 6346b7767694..eb985a6b5f93 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1883,7 +1883,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) } } - if (!vm::reservation_trylock(res, rtime)) + if (!vm::reservation_trylock(res, rtime).first) { return false; } @@ -2003,7 +2003,34 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) else { auto& data = vm::_ref(addr); - auto [res, time0] = vm::reservation_lock(addr, 128); + auto& res = vm::reservation_acquire(addr, 128); + + for(u64 time0 = UINT64_MAX & -128;;) + { + auto [ok, time1] = vm::reservation_trylock(res, res & -128, vm::putlluc_lockb); + + if (time0 & vm::putlluc_lockb && time1 > time0) + { + // Other PUTLLUC completed, don't execute + + if (ok) + { + res.release(time1 & -128); + } + + return; + } + + if (ok) + { + break; + } + + if (time1 & vm::putlluc_lockb) + { + time0 = time1; + } + } *reinterpret_cast*>(&data) += 0; diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 2555a449f96c..6a2399f78d1a 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -444,7 +444,7 @@ namespace vm { for (u64 i = 0;; i++) { - if (u64 rtime = res; !(rtime & 127) && reservation_trylock(res, rtime, lock_bits)) [[likely]] + if (u64 rtime = res; !(rtime & 127) && reservation_trylock(res, rtime, lock_bits).first) [[likely]] { return rtime; } diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index 6320c91e7d89..52810cef88d4 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -40,14 +40,14 @@ namespace vm u64 reservation_lock_internal(u32, atomic_t&, u64); - inline bool reservation_trylock(atomic_t& res, u64 rtime, u64 lock_bits = stcx_lockb) + inline std::pair reservation_trylock(atomic_t& res, u64 rtime, u64 lock_bits = stcx_lockb) { - if (res.compare_and_swap_test(rtime, rtime + lock_bits)) [[likely]] + if (res.compare_exchange(rtime, rtime + lock_bits)) [[likely]] { - return true; + return {true, rtime}; } - return false; + return {false, rtime}; } inline std::pair&, u64> reservation_lock(u32 addr, u32 size, u64 lock_bits = stcx_lockb) @@ -55,7 +55,7 @@ namespace vm auto res = &vm::reservation_acquire(addr, size); auto rtime = res->load(); - if (rtime & 127 || !reservation_trylock(*res, rtime, lock_bits)) [[unlikely]] + if (rtime & 127 || !reservation_trylock(*res, rtime, lock_bits).first) [[unlikely]] { static atomic_t no_lock{};