#include "stdafx.h" #include "rpcs3/Ini.h" #include "Utilities/Log.h" #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "Emu/IdManager.h" #include "CPUDecoder.h" #include "CPUThread.h" CPUThread::CPUThread(CPUThreadType type, const std::string& name, std::function thread_name) : m_id(idm::get_last_id()) , m_type(type) , m_name(name) { start(std::move(thread_name), [this] { Emu.SendDbgCommand(DID_CREATE_THREAD, this); std::unique_lock lock(mutex); // check thread status while (joinable() && is_alive()) { CHECK_EMU_STATUS; // check stop status if (!is_stopped()) { if (lock) lock.unlock(); try { task(); } catch (CPUThreadReturn) { ; } catch (CPUThreadStop) { m_state |= CPU_STATE_STOPPED; } catch (CPUThreadExit) { m_state |= CPU_STATE_DEAD; break; } catch (...) { dump_info(); throw; } m_state &= ~CPU_STATE_RETURN; continue; } if (!lock) { lock.lock(); continue; } cv.wait(lock); } }); } CPUThread::~CPUThread() { Emu.SendDbgCommand(DID_REMOVE_THREAD, this); } bool CPUThread::is_paused() const { return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused(); } void CPUThread::dump_info() const { if (!Emu.IsStopped()) { LOG_NOTICE(GENERAL, RegsToString()); } } void CPUThread::run() { Emu.SendDbgCommand(DID_START_THREAD, this); init_stack(); init_regs(); do_run(); Emu.SendDbgCommand(DID_STARTED_THREAD, this); } void CPUThread::pause() { Emu.SendDbgCommand(DID_PAUSE_THREAD, this); m_state |= CPU_STATE_PAUSED; Emu.SendDbgCommand(DID_PAUSED_THREAD, this); } void CPUThread::resume() { Emu.SendDbgCommand(DID_RESUME_THREAD, this); { // lock for reliable notification std::lock_guard lock(mutex); m_state &= ~CPU_STATE_PAUSED; cv.notify_one(); } Emu.SendDbgCommand(DID_RESUMED_THREAD, this); } void CPUThread::stop() { Emu.SendDbgCommand(DID_STOP_THREAD, this); if (is_current()) { throw CPUThreadStop{}; } else { // lock for reliable notification std::lock_guard lock(mutex); m_state |= CPU_STATE_STOPPED; cv.notify_one(); } Emu.SendDbgCommand(DID_STOPED_THREAD, this); } void CPUThread::exec() { Emu.SendDbgCommand(DID_EXEC_THREAD, this); { // lock for reliable notification std::lock_guard lock(mutex); m_state &= ~CPU_STATE_STOPPED; cv.notify_one(); } } void CPUThread::exit() { if (is_current()) { throw CPUThreadExit{}; } else { throw EXCEPTION("Unable to exit another thread"); } } void CPUThread::step() { if (m_state.atomic_op([](u64& state) -> bool { const bool was_paused = (state & CPU_STATE_PAUSED) != 0; state |= CPU_STATE_STEP; state &= ~CPU_STATE_PAUSED; return was_paused; })) { if (is_current()) return; // lock for reliable notification (only if PAUSE was removed) std::lock_guard lock(mutex); cv.notify_one(); } } void CPUThread::sleep() { m_state += CPU_STATE_MAX; m_state |= CPU_STATE_SLEEP; } void CPUThread::awake() { // must be called after the balanced Sleep() call if (m_state.atomic_op([](u64& state) -> bool { if (state < CPU_STATE_MAX) { throw EXCEPTION("Sleep()/Awake() inconsistency"); } if ((state -= CPU_STATE_MAX) < CPU_STATE_MAX) { state &= ~CPU_STATE_SLEEP; // notify the condition variable as well return true; } return false; })) { if (is_current()) return; // lock for reliable notification; the condition being checked is probably externally set std::lock_guard lock(mutex); cv.notify_one(); } } bool CPUThread::signal() { // try to set SIGNAL if (m_state._or(CPU_STATE_SIGNAL) & CPU_STATE_SIGNAL) { return false; } else { // not truly responsible for signal delivery, requires additional measures like LV2_LOCK cv.notify_one(); return true; } } bool CPUThread::unsignal() { // remove SIGNAL and return its old value return (m_state._and_not(CPU_STATE_SIGNAL) & CPU_STATE_SIGNAL) != 0; } bool CPUThread::check_status() { std::unique_lock lock(mutex, std::defer_lock); while (true) { CHECK_EMU_STATUS; // check at least once if (!is_paused() && (m_state & CPU_STATE_INTR) == 0) { break; } if (!lock) { lock.lock(); continue; } if (!is_paused() && (m_state & CPU_STATE_INTR) != 0 && handle_interrupt()) { continue; } cv.wait(lock); } if (m_state & CPU_STATE_RETURN || is_stopped()) { return true; } if (m_state & CPU_STATE_STEP) { // set PAUSE, but allow to execute once m_state |= CPU_STATE_PAUSED; m_state &= ~CPU_STATE_STEP; } return false; }