From 9677a3a9ea6ae677d89311152550b829cec1c334 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Thu, 23 Jan 2025 07:59:33 +0200 Subject: [PATCH] Fix Emulator::IsPaused() to allow measurements during module compilation Also fix a potential deadlock in access violation handler for non-cpu_thread --- Utilities/Thread.cpp | 49 +++++++++++++++++++++++---- rpcs3/Emu/Cell/Modules/cellAudio.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellCamera.cpp | 4 +-- rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp | 4 +-- rpcs3/Emu/Cell/lv2/sys_timer.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_uart.cpp | 2 +- rpcs3/Emu/GDB.cpp | 2 +- rpcs3/Emu/System.h | 3 +- 8 files changed, 52 insertions(+), 16 deletions(-) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 1d49f9c701f8..603c430ba3c8 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1703,23 +1703,58 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe cpu->state += cpu_flag::wait; } - Emu.Pause(true); - + // Note: a thread may access violate more than once after hack_alloc recovery + // Do not log any further access violations in this case. if (!g_tls_access_violation_recovered) { vm_log.notice("\n%s", dump_useful_thread_info()); + vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (cpu && cpu->get_class() == thread_class::ppu && cpu->get_pc() == addr ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); } - // Note: a thread may access violate more than once after hack_alloc recovery - // Do not log any further access violations in this case. - if (!g_tls_access_violation_recovered) + while (Emu.IsPausedOrReady()) { - vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (cpu && cpu->get_class() == thread_class::ppu && cpu->get_pc() == addr ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); + if (cpu) + { + auto state = +cpu->state; + + if (::is_paused(state) && !::is_stopped(state)) + { + thread_ctrl::wait_on(cpu->state, state); + } + else + { + // Temporary until Emulator updates state + std::this_thread::yield(); + } + } + else + { + thread_ctrl::wait_for(1000); + } } + Emu.Pause(true); + while (Emu.IsPaused()) { - thread_ctrl::wait(); + if (cpu) + { + auto state = +cpu->state; + + if (::is_paused(state) && !::is_stopped(state)) + { + thread_ctrl::wait_on(cpu->state, state); + } + else + { + // Temporary until Emulator updates state + std::this_thread::yield(); + } + } + else + { + thread_ctrl::wait_for(1000); + } } if (Emu.IsStopped() && !hack_alloc()) diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index ceeb02da6340..ebb5b134e401 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -704,7 +704,7 @@ void cell_audio_thread::operator()() thread_ctrl::scoped_priority high_prio(+1); - while (Emu.IsPaused()) + while (Emu.IsPausedOrReady()) { thread_ctrl::wait_for(5000); } diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 95cf0a56f99d..39274e8162ba 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -1640,14 +1640,14 @@ void camera_context::operator()() while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { // send ATTACH event - if (init && is_attached_dirty && !Emu.IsPaused()) + if (init && is_attached_dirty && !Emu.IsPausedOrReady()) { send_attach_state(is_attached); } const s32 fps = info.framerate; - if (!init || !fps || Emu.IsPaused() || g_cfg.io.camera == camera_handler::null) + if (!init || !fps || Emu.IsPausedOrReady() || g_cfg.io.camera == camera_handler::null) { thread_ctrl::wait_for(1000); // hack continue; diff --git a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp index 5f06415ac7fe..290f4679417a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp @@ -825,7 +825,7 @@ void rsxaudio_data_thread::extract_audio_data() return rsxaudio_obj_ptr; }(); - if (Emu.IsPaused() || !rsxaudio_obj) + if (Emu.IsPausedOrReady() || !rsxaudio_obj) { advance_all_timers(); return; @@ -1533,7 +1533,7 @@ void rsxaudio_backend_thread::operator()() backend_failed = false; } - if (!Emu.IsPaused() || !use_aux_ringbuf) // Don't pause if thread is in direct mode + if (!Emu.IsPausedOrReady() || !use_aux_ringbuf) // Don't pause if thread is in direct mode { if (!backend_playing()) { diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.cpp b/rpcs3/Emu/Cell/lv2/sys_timer.cpp index b4b3b780f2f8..5e6c3d20cb08 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_timer.cpp @@ -135,7 +135,7 @@ void lv2_timer_thread::operator()() sleep_time = umax; - if (Emu.IsPaused()) + if (Emu.IsPausedOrReady()) { sleep_time = 10000; continue; diff --git a/rpcs3/Emu/Cell/lv2/sys_uart.cpp b/rpcs3/Emu/Cell/lv2/sys_uart.cpp index 3bf5f6a0c030..67e91b97a0e7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_uart.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_uart.cpp @@ -1968,7 +1968,7 @@ void vuart_av_thread::operator()() { while (thread_ctrl::state() != thread_state::aborting) { - if (Emu.IsPaused()) + if (Emu.IsPausedOrReady()) { thread_ctrl::wait_for(5000); continue; diff --git a/rpcs3/Emu/GDB.cpp b/rpcs3/Emu/GDB.cpp index 56ad52705aa9..dc01e4e3fe55 100644 --- a/rpcs3/Emu/GDB.cpp +++ b/rpcs3/Emu/GDB.cpp @@ -830,7 +830,7 @@ bool gdb_thread::cmd_vcont(gdb_cmd& cmd) { Emu.Run(true); } - if (Emu.IsPaused()) + else if (Emu.IsPaused()) { Emu.Resume(); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index bd127aff4f1f..f9d0546024ea 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -426,7 +426,8 @@ class Emulator final static void CleanUp(); bool IsRunning() const { return m_state == system_state::running; } - bool IsPaused() const { return m_state >= system_state::paused; } // ready/starting are also considered paused by this function + bool IsPaused() const { system_state state = m_state; return state >= system_state::paused && state <= system_state::frozen; } + bool IsPausedOrReady() const { return m_state >= system_state::paused; } bool IsStopped(bool test_fully = false) const { return test_fully ? m_state == system_state::stopped : m_state <= system_state::stopping; } bool IsReady() const { return m_state == system_state::ready; } bool IsStarting() const { return m_state == system_state::starting; }