diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 54731c4454a9..ea61c328adf0 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -276,8 +276,8 @@ class thread_ctrl final } // Wait for both thread sync var and provided atomic var - template - static inline void wait_on(T& wait, U old, u64 usec = -1) + template + static inline void wait_on_custom(Func&& setter, u64 usec = -1) { auto _this = g_tls_this_thread; @@ -286,13 +286,19 @@ class thread_ctrl final return; } - atomic_wait::list<3> list{}; - list.set<0, Op>(wait, old); - list.set<1>(_this->m_sync, 0, 4 + 1); - list.set<2>(_this->m_taskq, nullptr); + atomic_wait::list list{}; + list.set(_this->m_sync, 0, 4 + 1); + list.set(_this->m_taskq, nullptr); + setter(list); list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); } + template + static inline void wait_on(T& wait, U old, u64 usec = -1) + { + wait_on_custom<1>([&](L& list){ list.set<0, Op>(wait, old); }, usec); + } + // Exit. [[noreturn]] static void emergency_exit(std::string_view reason); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index e67464493aa2..3acda94a7c92 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4022,40 +4022,45 @@ s64 spu_thread::get_ch_value(u32 ch) spu_function_logger logger(*this, "MFC Events read"); - if (mask1 & SPU_EVENT_LR && raddr) - { - if (mask1 != SPU_EVENT_LR && mask1 != SPU_EVENT_LR + SPU_EVENT_TM) - { - // Combining LR with other flags needs another solution - fmt::throw_exception("Not supported: event mask 0x%x", mask1); - } + state += cpu_flag::wait; - for (; !events.count; events = get_events(mask1, false, true)) + using resrv_ptr = std::add_pointer_t; + + resrv_ptr resrv_mem{}; + std::shared_ptr rdata_shm; + + if (raddr && mask1 & SPU_EVENT_LR) + { + if (auto area = vm::get(vm::any, raddr)) { - const auto old = state.add_fetch(cpu_flag::wait); + // Ensure possesion over reservation memory so it won't be deallocated + auto [base_addr, shm_] = area->peek(raddr); - if (is_stopped(old)) + if (shm_) { - return -1; + const u32 data_offs = raddr - base_addr; + rdata_shm = std::move(shm_); + vm::writer_lock{}, resrv_mem = reinterpret_cast(rdata_shm->map_self() + data_offs); } - - if (is_paused(old)) + else { - // Ensure reservation data won't change while paused for debugging purposes - check_state(); - continue; + raddr = 0; } - - vm::reservation_notifier(raddr).wait(rtime, -128, atomic_wait_timeout{100'000}); + } + else + { + raddr = 0; } - check_state(); - return events.events & mask1; + if (!raddr) + { + set_events(SPU_EVENT_LR); + } } - for (; !events.count; events = get_events(mask1, true, true)) + for (; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true)) { - const auto old = state.add_fetch(cpu_flag::wait); + const auto old = +state; if (is_stopped(old)) { @@ -4064,7 +4069,35 @@ s64 spu_thread::get_ch_value(u32 ch) if (is_paused(old)) { + // Ensure spu_thread::rdata's stagnancy while the thread is paused for debugging purposes check_state(); + state += cpu_flag::wait; + continue; + } + + // Optimized check + if (raddr && (!vm::check_addr(raddr) || rtime != vm::reservation_acquire(raddr) || !cmp_rdata(rdata, *resrv_mem)))) + { + raddr = 0; + set_events(SPU_EVENT_LR); + continue; + } + + if (g_cfg.core.spu_alerted_rdata_check) + { + // Optimized for wake-up speed when simple writes are the cause of reservation loss + // Benefit is rare, as you need many threads to experience it + the game also needs to use STW-alike instructions to invalidate reservation + continue; + } + + if (raddr) + { + thread_ctrl::wait_on_custom<2>([&](T& list) + { + list.set<0>(state, old); + list.set<1>(vm::reservation_notifier(raddr), rtime, -128); + }, 100); + continue; } diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 82197099a2f5..0fd1dbf3b1c0 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -1422,10 +1422,9 @@ namespace vm return {addr, nullptr}; } - // Special case if (m_common) { - return {addr, nullptr}; + return {this->addr, m_common}; } // Range check diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index bb8c5dd8b999..03d335421afa 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -46,6 +46,7 @@ struct cfg_root : cfg::node cfg::_bool spu_verification{ this, "SPU Verification", true }; // Should be enabled cfg::_bool spu_cache{ this, "SPU Cache", true }; cfg::_bool spu_prof{ this, "SPU Profiler", false }; + cfg::_bool spu_alerted_rdata_check{ this, "SPU Alerted Reservation Check", false, true }; // Do not use if unsure, refer to SPUThread.cpp for explenation cfg::uint<0, 16> mfc_transfers_shuffling{ this, "MFC Commands Shuffling Limit", 0 }; cfg::uint<0, 10000> mfc_transfers_timeout{ this, "MFC Commands Timeout", 0, true }; cfg::_bool mfc_shuffling_in_steps{ this, "MFC Commands Shuffling In Steps", false, true };