diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 9668aadee9..625080a3ac 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3256,6 +3256,8 @@ const auto ppu_stcx_accurate_tx = build_function_asm& get_resrv_waiters_count(u32 raddr); + template static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) { @@ -3500,19 +3502,30 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) if (notify) { - vm::reservation_notifier(notify).notify_all(); + bool notified = false; + + if (ppu.res_notify_time == (vm::reservation_acquire(notify) & -128)) + { + vm::reservation_notifier(notify).notify_all(); + notified = true; + } + + if (!notified || (addr ^ notify) & -128) + { + res.notify_all(); + } + ppu.res_notify = 0; } - - if (!notify) + else { // Try to postpone notification to when PPU is asleep or join notifications on the same address // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock - ppu.res_notify = addr; - } - else if ((addr ^ notify) & -128) - { - res.notify_all(); + if (get_resrv_waiters_count(addr)) + { + ppu.res_notify = addr; + ppu.res_notify_time = rtime + 128; + } } } @@ -3531,7 +3544,11 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) // And on failure it has some time to do something else if (notify && ((addr ^ notify) & -128)) { - vm::reservation_notifier(notify).notify_all(); + if (ppu.res_notify_time == (vm::reservation_acquire(notify) & -128)) + { + vm::reservation_notifier(notify).notify_all(); + } + ppu.res_notify = 0; } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 5e7a430fba..903262652d 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -262,7 +262,8 @@ public: u64 rtime{0}; alignas(64) std::byte rdata[128]{}; // Reservation data bool use_full_rdata{}; - u32 res_notify{}; + u32 res_notify{0}; + u64 res_notify_time{0}; union ppu_prio_t { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 79a6336e61..aca27ca70a 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -446,10 +446,10 @@ waitpkg_func static void __tpause(u32 cycles, u32 cstate) static std::array, 128> g_resrv_waiters_count; -static inline atomic_t& get_resrv_waiters_count(u32 raddr) +extern atomic_t& get_resrv_waiters_count(u32 raddr) { // Storage efficient method to distinguish different nearby addresses (which are likely) - return g_resrv_waiters_count[std::popcount(raddr) + ((raddr / 128) % 4) * 32]; + return g_resrv_waiters_count[std::popcount(raddr & -512) + ((raddr / 128) % 4) * 32]; } void do_cell_atomic_128_store(u32 addr, const void* to_write); diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 27edcc5466..0c2308dd42 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1333,9 +1333,12 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) { static_cast(cpu).res_notify = 0; - const usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); - - if (notify_later_idx != umax) + if (static_cast(cpu).res_notify_time != (vm::reservation_acquire(addr) & -128)) + { + // Ignore outdated notification request + } + else if (usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); + notify_later_idx != umax) { g_to_notify[notify_later_idx] = &vm::reservation_notifier(addr); @@ -1384,9 +1387,12 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio) { ppu->res_notify = 0; - const usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); - - if (notify_later_idx != umax) + if (ppu->res_notify_time != (vm::reservation_acquire(addr) & -128)) + { + // Ignore outdated notification request + } + else if (usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); + notify_later_idx != umax) { g_to_notify[notify_later_idx] = &vm::reservation_notifier(addr);