From ca6783ba9ac0335a8490631cf2b73fd4aef81b6d Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Thu, 26 Nov 2015 11:06:29 +0300 Subject: [PATCH] Threads improved, ID manager improved --- Utilities/Log.cpp | 2 +- Utilities/SleepQueue.cpp | 6 +- Utilities/SleepQueue.h | 11 +- Utilities/Thread.cpp | 252 ++++---- Utilities/Thread.h | 178 ++++-- rpcs3/Emu/ARMv7/ARMv7Thread.cpp | 14 +- rpcs3/Emu/ARMv7/ARMv7Thread.h | 3 +- rpcs3/Emu/CPU/CPUThread.cpp | 138 ++-- rpcs3/Emu/CPU/CPUThread.h | 61 +- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 9 +- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 6 +- rpcs3/Emu/Cell/PPUThread.cpp | 20 +- rpcs3/Emu/Cell/PPUThread.h | 3 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 19 +- rpcs3/Emu/Cell/RawSPUThread.h | 3 +- rpcs3/Emu/Cell/SPUThread.cpp | 29 +- rpcs3/Emu/Cell/SPUThread.h | 5 +- rpcs3/Emu/IdManager.cpp | 155 ++++- rpcs3/Emu/IdManager.h | 598 +++++++++--------- rpcs3/Emu/Memory/vm.cpp | 49 +- rpcs3/Emu/Memory/vm.h | 7 +- rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp | 4 +- rpcs3/Emu/RSX/D3D12/D3D12GSRender.h | 4 +- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 8 +- rpcs3/Emu/RSX/GL/GLGSRender.h | 6 +- rpcs3/Emu/RSX/GSManager.cpp | 19 +- rpcs3/Emu/RSX/GSManager.h | 6 +- rpcs3/Emu/RSX/GSRender.cpp | 20 +- rpcs3/Emu/RSX/GSRender.h | 5 +- rpcs3/Emu/RSX/Null/NullGSRender.cpp | 6 +- rpcs3/Emu/RSX/Null/NullGSRender.h | 3 +- rpcs3/Emu/RSX/RSXThread.cpp | 192 +++--- rpcs3/Emu/RSX/RSXThread.h | 15 +- rpcs3/Emu/SysCalls/Modules/cellAudio.cpp | 34 +- rpcs3/Emu/SysCalls/Modules/cellAudio.h | 16 +- rpcs3/Emu/SysCalls/Modules/cellFs.cpp | 8 +- rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp | 11 +- rpcs3/Emu/SysCalls/Modules/sys_net.cpp | 2 +- .../Emu/SysCalls/Modules/sys_ppu_thread_.cpp | 10 +- rpcs3/Emu/SysCalls/lv2/sys_cond.cpp | 2 +- rpcs3/Emu/SysCalls/lv2/sys_fs.h | 2 +- rpcs3/Emu/SysCalls/lv2/sys_mutex.cpp | 4 +- rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp | 12 +- rpcs3/Emu/SysCalls/lv2/sys_rwlock.cpp | 4 +- rpcs3/Emu/SysCalls/lv2/sys_timer.cpp | 77 +-- rpcs3/Emu/SysCalls/lv2/sys_timer.h | 31 +- rpcs3/Emu/System.h | 27 +- rpcs3/stdafx.h | 7 +- 48 files changed, 1113 insertions(+), 990 deletions(-) diff --git a/Utilities/Log.cpp b/Utilities/Log.cpp index 4b981756a2..6fa770f0c1 100644 --- a/Utilities/Log.cpp +++ b/Utilities/Log.cpp @@ -199,7 +199,7 @@ void LogManager::log(LogMessage msg) prefix = "E "; break; } - if (auto thr = get_current_thread_ctrl()) + if (auto thr = thread_ctrl::get_current()) { prefix += "{" + thr->get_name() + "} "; } diff --git a/Utilities/SleepQueue.cpp b/Utilities/SleepQueue.cpp index 9847203328..d7f432cea6 100644 --- a/Utilities/SleepQueue.cpp +++ b/Utilities/SleepQueue.cpp @@ -5,7 +5,7 @@ void sleep_queue_entry_t::add_entry() { - m_queue.emplace_back(m_thread.shared_from_this()); + m_queue.emplace_back(std::static_pointer_cast(m_thread.shared_from_this())); } void sleep_queue_entry_t::remove_entry() @@ -33,7 +33,7 @@ bool sleep_queue_entry_t::find() const return false; } -sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue) +sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue) : m_thread(cpu) , m_queue(queue) { @@ -41,7 +41,7 @@ sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue) cpu.sleep(); } -sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue, const defer_sleep_t&) +sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue, const defer_sleep_t&) : m_thread(cpu) , m_queue(queue) { diff --git a/Utilities/SleepQueue.h b/Utilities/SleepQueue.h index db54602038..e66a284c65 100644 --- a/Utilities/SleepQueue.h +++ b/Utilities/SleepQueue.h @@ -1,15 +1,14 @@ #pragma once -class CPUThread; - -using sleep_queue_t = std::deque>; +using sleep_entry_t = class CPUThread; +using sleep_queue_t = std::deque>; static struct defer_sleep_t {} const defer_sleep{}; // automatic object handling a thread entry in the sleep queue class sleep_queue_entry_t final { - CPUThread& m_thread; + sleep_entry_t& m_thread; sleep_queue_t& m_queue; void add_entry(); @@ -18,10 +17,10 @@ class sleep_queue_entry_t final public: // add specified thread to the sleep queue - sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue); + sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue); // don't add specified thread to the sleep queue - sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue, const defer_sleep_t&); + sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue, const defer_sleep_t&); // removes specified thread from the sleep queue if added ~sleep_queue_entry_t(); diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index ab0c1ed7fb..8afac01586 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -19,6 +19,27 @@ #include #endif +static const auto s_terminate_handler_set = std::set_terminate([]() +{ + if (std::uncaught_exception()) + { + try + { + throw; + } + catch (const std::exception& ex) + { + std::printf("Unhandled exception: %s\n", ex.what()); + } + catch (...) + { + std::printf("Unhandled exception of unknown type.\n"); + } + } + + std::abort(); +}); + void SetCurrentThreadDebugName(const char* threadName) { #if defined(_MSC_VER) // this is VS-specific way to set thread names for the debugger @@ -113,10 +134,11 @@ enum x64_op_t : u32 X64OP_STOS, X64OP_XCHG, X64OP_CMPXCHG, - X64OP_LOAD_AND_STORE, // lock and [mem],reg - X64OP_LOAD_OR_STORE, // TODO: lock or [mem], reg - X64OP_INC, // TODO: lock inc [mem] - X64OP_DEC, // TODO: lock dec [mem] + X64OP_LOAD_AND_STORE, // lock and [mem], reg + X64OP_LOAD_OR_STORE, // lock or [mem], reg (TODO) + X64OP_LOAD_XOR_STORE, // lock xor [mem], reg (TODO) + X64OP_INC, // lock inc [mem] (TODO) + X64OP_DEC, // lock dec [mem] (TODO) }; void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, size_t& out_size, size_t& out_length) @@ -1132,10 +1154,7 @@ const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(excep const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0); const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0; - if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && - (u32)addr64 == addr64 && - get_current_thread_ctrl() && - handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord)) + if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && (u32)addr64 == addr64 && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord)) { return EXCEPTION_CONTINUE_EXECUTION; } @@ -1164,7 +1183,7 @@ void signal_handler(int sig, siginfo_t* info, void* uct) const bool is_writing = ((ucontext_t*)uct)->uc_mcontext.gregs[REG_ERR] & 0x2; #endif - if ((u32)addr64 == addr64 && get_current_thread_ctrl()) + if ((u32)addr64 == addr64 && thread_ctrl::get_current()) { if (handle_access_violation((u32)addr64, is_writing, (ucontext_t*)uct)) { @@ -1191,94 +1210,112 @@ const int sigaction_result = []() -> int #endif -thread_local thread_ctrl_t* g_tls_this_thread = nullptr; +thread_local thread_ctrl* thread_ctrl::g_tls_this_thread = nullptr; -const thread_ctrl_t* get_current_thread_ctrl() -{ - return g_tls_this_thread; -} +// TODO +std::atomic g_thread_count{ 0 }; -std::string thread_ctrl_t::get_name() const +void thread_ctrl::initialize() { - return m_name(); -} + SetCurrentThreadDebugName(g_tls_this_thread->m_name().c_str()); -named_thread_t::named_thread_t(std::function name, std::function func) -{ - start(std::move(name), std::move(func)); -} +#if defined(_MSC_VER) + _set_se_translator(_se_translator); // not essential, disable if necessary +#endif -named_thread_t::~named_thread_t() -{ - if (m_thread) +#ifdef _WIN32 + if (!exception_handler || !exception_filter) +#else + if (sigaction_result == -1) +#endif { - std::printf("Fatal: thread neither joined nor detached\n"); + std::printf("Exceptions handlers are not set correctly.\n"); std::terminate(); } + + // TODO + g_thread_count++; +} + +void thread_ctrl::finalize() noexcept +{ + // TODO + vm::reservation_free(); + + // TODO + g_thread_count--; + + // Call atexit functions + for (const auto& func : decltype(m_atexit)(std::move(g_tls_this_thread->m_atexit))) + { + func(); + } +} + +thread_ctrl::~thread_ctrl() +{ + m_thread.detach(); + + if (m_future.valid()) + { + try + { + m_future.get(); + } + catch (const std::exception& ex) + { + LOG_ERROR(GENERAL, "Abandoned exception: %s", ex.what()); + } + catch (EmulationStopped) + { + } + } +} + +std::string thread_ctrl::get_name() const +{ + CHECK_ASSERTION(m_name); + + return m_name(); } std::string named_thread_t::get_name() const { - if (!m_thread) - { - throw EXCEPTION("Invalid thread"); - } - - if (!m_thread->m_name) - { - throw EXCEPTION("Invalid name getter"); - } - - return m_thread->m_name(); + return fmt::format("('%s') Unnamed Thread", typeid(*this).name()); } -std::atomic g_thread_count{ 0 }; - -void named_thread_t::start(std::function name, std::function func) +void named_thread_t::start() { - if (m_thread) + CHECK_ASSERTION(m_thread == nullptr); + + // Get shared_ptr instance (will throw if called from the constructor or the object has been created incorrectly) + auto ptr = shared_from_this(); + + // Make name getter + auto name = [wptr = std::weak_ptr(ptr), type = &typeid(*this)]() { - throw EXCEPTION("Thread already exists"); - } + // Return actual name if available + if (const auto ptr = wptr.lock()) + { + return ptr->get_name(); + } + else + { + return fmt::format("('%s') Deleted Thread", type->name()); + } + }; - // create new thread control variable - m_thread = std::make_shared(std::move(name)); - - // start thread - m_thread->m_thread = std::thread([](std::shared_ptr ctrl, std::function func) + // Run thread + m_thread = thread_ctrl::spawn(std::move(name), [thread = std::move(ptr)]() { - g_tls_this_thread = ctrl.get(); - - SetCurrentThreadDebugName(ctrl->get_name().c_str()); - -#if defined(_MSC_VER) - _set_se_translator(_se_translator); -#endif - -#ifdef _WIN32 - if (!exception_handler || !exception_filter) - { - LOG_ERROR(GENERAL, "exception_handler not set"); - return; - } -#else - if (sigaction_result == -1) - { - printf("sigaction() failed"); - exit(EXIT_FAILURE); - } -#endif - try { - g_thread_count++; - if (rpcs3::config.misc.log.hle_logging.value()) { LOG_NOTICE(GENERAL, "Thread started"); } - func(); + thread->on_task(); if (rpcs3::config.misc.log.hle_logging.value()) { @@ -1295,75 +1332,24 @@ void named_thread_t::start(std::function name, std::functionm_atexit) - { - func(); - - func = nullptr; - } - - vm::reservation_free(); - - g_thread_count--; - - }, m_thread, std::move(func)); -} - -void named_thread_t::detach() -{ - if (!m_thread) - { - throw EXCEPTION("Invalid thread"); - } - - // +clear m_thread - const auto ctrl = std::move(m_thread); - - // notify if detached by another thread - if (g_tls_this_thread != m_thread.get()) - { - // lock for reliable notification - std::lock_guard lock(mutex); - - cv.notify_one(); - } - - ctrl->m_thread.detach(); + thread->on_exit(); + }); } void named_thread_t::join() { - if (!m_thread) + CHECK_ASSERTION(m_thread != nullptr); + + try { - throw EXCEPTION("Invalid thread"); + m_thread->join(); + m_thread.reset(); } - - if (g_tls_this_thread == m_thread.get()) + catch (...) { - throw EXCEPTION("Deadlock"); + m_thread.reset(); + throw; } - - // +clear m_thread - const auto ctrl = std::move(m_thread); - - { - // lock for reliable notification - std::lock_guard lock(mutex); - - cv.notify_one(); - } - - ctrl->m_thread.join(); -} - -bool named_thread_t::is_current() const -{ - if (!m_thread) - { - throw EXCEPTION("Invalid thread"); - } - - return g_tls_this_thread == m_thread.get(); } const std::function SQUEUE_ALWAYS_EXIT = [](){ return true; }; diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 34b65e4763..1ceb587e73 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -1,107 +1,171 @@ #pragma once -const class thread_ctrl_t* get_current_thread_ctrl(); - -// Named thread control class -class thread_ctrl_t final +// Thread control class +class thread_ctrl final { - friend class named_thread_t; - - template friend void current_thread_register_atexit(T); - - // Thread handler - std::thread m_thread; + static thread_local thread_ctrl* g_tls_this_thread; // Name getter - const std::function m_name; + std::function m_name; - // Functions executed at thread exit (temporarily) - std::vector> m_atexit; + // Thread handle (be careful) + std::thread m_thread; + + // Thread result + std::future m_future; + + // Functions scheduled at thread exit + std::deque> m_atexit; + + // Called at the thread start + static void initialize(); + + // Called at the thread end + static void finalize() noexcept; public: - thread_ctrl_t(std::function name) - : m_name(std::move(name)) + template + thread_ctrl(T&& name) + : m_name(std::forward(name)) { } - thread_ctrl_t(const thread_ctrl_t&) = delete; + // Disable copy/move constructors and operators + thread_ctrl(const thread_ctrl&) = delete; + + ~thread_ctrl(); // Get thread name std::string get_name() const; + + // Get future result (may throw) + void join() + { + return m_future.get(); + } + + // Get current thread (may be nullptr) + static const thread_ctrl* get_current() + { + return g_tls_this_thread; + } + + // Register function at thread exit (for the current thread) + template + static inline void at_exit(T&& func) + { + CHECK_ASSERTION(g_tls_this_thread); + + g_tls_this_thread->m_atexit.emplace_front(std::forward(func)); + } + + // Named thread factory + template + static inline std::shared_ptr spawn(N&& name, F&& func) + { + auto ctrl = std::make_shared(std::forward(name)); + + std::promise promise; + + ctrl->m_future = promise.get_future(); + + ctrl->m_thread = std::thread([ctrl, task = std::forward(func)](std::promise promise) + { + g_tls_this_thread = ctrl.get(); + + try + { + initialize(); + task(); + finalize(); + promise.set_value(); + } + catch (...) + { + finalize(); + promise.set_exception(std::current_exception()); + } + + }, std::move(promise)); + + return ctrl; + } }; -// Register function at thread exit (temporarily) -template void current_thread_register_atexit(T func) -{ - extern thread_local thread_ctrl_t* g_tls_this_thread; - - g_tls_this_thread->m_atexit.emplace_back(func); -} - -class named_thread_t +class named_thread_t : public std::enable_shared_from_this { // Pointer to managed resource (shared with actual thread) - std::shared_ptr m_thread; + std::shared_ptr m_thread; public: - // Thread mutex for external use - std::mutex mutex; - - // Thread condition variable for external use + // Thread condition variable for external use (this thread waits on it, other threads may notify) std::condition_variable cv; + // Thread mutex for external use (can be used with `cv`) + std::mutex mutex; + +protected: + // Thread task (called in the thread) + virtual void on_task() = 0; + + // Thread finalization (called after on_task) + virtual void on_exit() {} + + // ID initialization (called through id_aux_initialize) + virtual void on_id_aux_initialize() { start(); } + + // ID finalization (called through id_aux_finalize) + virtual void on_id_aux_finalize() { join(); } + public: - // Initialize in empty state named_thread_t() = default; - // Create named thread - named_thread_t(std::function name, std::function func); + virtual ~named_thread_t() = default; // Deleted copy/move constructors + copy/move operators named_thread_t(const named_thread_t&) = delete; - // Destructor, calls std::terminate if the thread is neither joined nor detached - virtual ~named_thread_t(); - -public: // Get thread name - std::string get_name() const; + virtual std::string get_name() const; - // Create named thread (current state must be empty) - void start(std::function name, std::function func); + // Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case) + void start(); - // Detach thread -> empty state - void detach(); - - // Join thread -> empty state + // Join thread (get future result) void join(); // Check whether the thread is not in "empty state" - bool joinable() const { return m_thread.operator bool(); } + bool is_started() const { return m_thread.operator bool(); } - // Check whether it is the currently running thread - bool is_current() const; + // Compare with the current thread + bool is_current() const { CHECK_ASSERTION(m_thread); return thread_ctrl::get_current() == m_thread.get(); } - // Get internal thread pointer - const thread_ctrl_t* get_thread_ctrl() const { return m_thread.get(); } + // Get thread_ctrl + const thread_ctrl* get_thread_ctrl() const { return m_thread.get(); } + + friend void id_aux_initialize(named_thread_t* ptr) { ptr->on_id_aux_initialize(); } + friend void id_aux_finalize(named_thread_t* ptr) { ptr->on_id_aux_finalize(); } }; -// Wrapper for named_thread_t, joins automatically in the destructor -class autojoin_thread_t final +// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope +class scope_thread_t final { - named_thread_t m_thread; + std::shared_ptr m_thread; public: - autojoin_thread_t(std::function name, std::function func) - : m_thread(std::move(name), std::move(func)) + template + scope_thread_t(N&& name, F&& func) + : m_thread(thread_ctrl::spawn(std::forward(name), std::forward(func))) { } - autojoin_thread_t(const autojoin_thread_t&) = delete; + // Deleted copy/move constructors + copy/move operators + scope_thread_t(const scope_thread_t&) = delete; - ~autojoin_thread_t() noexcept(false) // Allow exceptions + // Destructor with exceptions allowed + ~scope_thread_t() noexcept(false) { - m_thread.join(); + m_thread->join(); } }; diff --git a/rpcs3/Emu/ARMv7/ARMv7Thread.cpp b/rpcs3/Emu/ARMv7/ARMv7Thread.cpp index e856ce8722..8515ba5b4b 100644 --- a/rpcs3/Emu/ARMv7/ARMv7Thread.cpp +++ b/rpcs3/Emu/ARMv7/ARMv7Thread.cpp @@ -80,20 +80,22 @@ void armv7_free_tls(u32 thread) } ARMv7Thread::ARMv7Thread(const std::string& name) - : CPUThread(CPU_THREAD_ARMv7, name, WRAP_EXPR(fmt::format("ARMv7[0x%x] Thread (%s)[0x%08x]", m_id, m_name.c_str(), PC))) + : CPUThread(CPU_THREAD_ARMv7, name) , ARMv7Context({}) { } ARMv7Thread::~ARMv7Thread() { - cv.notify_one(); - join(); - close_stack(); armv7_free_tls(m_id); } +std::string ARMv7Thread::get_name() const +{ + return fmt::format("ARMv7 Thread[0x%x] (%s)[0x%08x]", m_id, CPUThread::get_name(), PC); +} + void ARMv7Thread::dump_info() const { if (hle_func) @@ -191,7 +193,7 @@ void ARMv7Thread::do_run() } } -void ARMv7Thread::task() +void ARMv7Thread::cpu_task() { if (custom_task) { @@ -225,7 +227,7 @@ void ARMv7Thread::fast_call(u32 addr) try { - task(); + cpu_task(); } catch (CPUThreadReturn) { diff --git a/rpcs3/Emu/ARMv7/ARMv7Thread.h b/rpcs3/Emu/ARMv7/ARMv7Thread.h index 93b611c0be..878afb1aa6 100644 --- a/rpcs3/Emu/ARMv7/ARMv7Thread.h +++ b/rpcs3/Emu/ARMv7/ARMv7Thread.h @@ -11,11 +11,12 @@ public: ARMv7Thread(const std::string& name); virtual ~ARMv7Thread() override; + virtual std::string get_name() const override; virtual void dump_info() const override; virtual u32 get_pc() const override { return PC; } virtual u32 get_offset() const override { return 0; } virtual void do_run() override; - virtual void task() override; + virtual void cpu_task() override; virtual void init_regs() override; virtual void init_stack() override; diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 01cca18224..d847666a42 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -9,65 +9,66 @@ thread_local CPUThread* g_tls_current_cpu_thread = nullptr; -CPUThread::CPUThread(CPUThreadType type, const std::string& name, std::function thread_name) +void CPUThread::on_task() +{ + g_tls_current_cpu_thread = this; + + Emu.SendDbgCommand(DID_CREATE_THREAD, this); + + std::unique_lock lock(mutex); + + // check thread status + while (is_alive()) + { + CHECK_EMU_STATUS; + + // check stop status + if (!is_stopped()) + { + if (lock) lock.unlock(); + + try + { + cpu_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(CPUThreadType type, const std::string& name) : m_id(idm::get_last_id()) , m_type(type) , m_name(name) { - start(std::move(thread_name), [this] - { - g_tls_current_cpu_thread = 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() @@ -75,6 +76,11 @@ CPUThread::~CPUThread() Emu.SendDbgCommand(DID_REMOVE_THREAD, this); } +std::string CPUThread::get_name() const +{ + return m_name; +} + bool CPUThread::is_paused() const { return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused(); @@ -149,25 +155,26 @@ void CPUThread::exec() { Emu.SendDbgCommand(DID_EXEC_THREAD, this); + m_state &= ~CPU_STATE_STOPPED; + { // lock for reliable notification std::lock_guard lock(mutex); - m_state &= ~CPU_STATE_STOPPED; - cv.notify_one(); } } void CPUThread::exit() { - if (is_current()) + m_state |= CPU_STATE_DEAD; + + if (!is_current()) { - throw CPUThreadExit{}; - } - else - { - throw EXCEPTION("Unable to exit another thread"); + // lock for reliable notification + std::lock_guard lock(mutex); + + cv.notify_one(); } } @@ -259,6 +266,11 @@ bool CPUThread::check_status() { CHECK_EMU_STATUS; // check at least once + if (!is_alive()) + { + return true; + } + if (!is_paused() && (m_state & CPU_STATE_INTR) == 0) { break; diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 25e4d4d914..1cfdd0e793 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -15,52 +15,44 @@ enum : u64 { CPU_STATE_STOPPED = (1ull << 0), // basic execution state (stopped by default), removed by Exec() CPU_STATE_PAUSED = (1ull << 1), // pauses thread execution, set by the debugger (manually or after step execution) - CPU_STATE_SLEEP = (1ull << 2), // shouldn't affect thread execution, set by Sleep() call, removed by the latest Awake() call, may possibly indicate waiting state of the thread + CPU_STATE_SLEEP = (1ull << 2), // shouldn't affect thread execution, set by sleep(), removed by the latest awake(), may possibly indicate waiting state of the thread CPU_STATE_STEP = (1ull << 3), // forces the thread to pause after executing just one instruction or something appropriate, set by the debugger CPU_STATE_DEAD = (1ull << 4), // indicates irreversible exit of the thread CPU_STATE_RETURN = (1ull << 5), // used for callback return CPU_STATE_SIGNAL = (1ull << 6), // used for HLE signaling CPU_STATE_INTR = (1ull << 7), // thread interrupted - CPU_STATE_MAX = (1ull << 8), // added to (subtracted from) m_state by Sleep()/Awake() calls to trigger status check + CPU_STATE_MAX = (1ull << 8), // added to (subtracted from) m_state by sleep()/awake() calls to trigger status check }; -// "HLE return" exception event -class CPUThreadReturn {}; - -// CPUThread::Stop exception event -class CPUThreadStop {}; - -// CPUThread::Exit exception event -class CPUThreadExit {}; +class CPUThreadReturn {}; // "HLE return" exception event +class CPUThreadStop {}; // CPUThread::Stop exception event +class CPUThreadExit {}; // CPUThread::Exit exception event class CPUDecoder; -class CPUThread : public named_thread_t, public std::enable_shared_from_this +class CPUThread : public named_thread_t { - using named_thread_t::start; + void on_task() override; + void on_id_aux_finalize() override { exit(); } // call exit() instead of join() protected: - using named_thread_t::detach; - using named_thread_t::join; - using named_thread_t::joinable; - atomic_t m_state{ CPU_STATE_STOPPED }; // thread state flags std::unique_ptr m_dec; const u32 m_id; const CPUThreadType m_type; - const std::string m_name; // changing m_name would be terribly thread-unsafe in current implementation + const std::string m_name; // changing m_name is unsafe because it can be read at any moment - CPUThread(CPUThreadType type, const std::string& name, std::function thread_name); + CPUThread(CPUThreadType type, const std::string& name); public: virtual ~CPUThread() override; + virtual std::string get_name() const override; u32 get_id() const { return m_id; } CPUThreadType get_type() const { return m_type; } - std::string get_name() const { return m_name; } bool is_alive() const { return (m_state & CPU_STATE_DEAD) == 0; } bool is_stopped() const { return (m_state & CPU_STATE_STOPPED) != 0; } @@ -70,7 +62,7 @@ public: virtual u32 get_pc() const = 0; virtual u32 get_offset() const = 0; virtual void do_run() = 0; - virtual void task() = 0; + virtual void cpu_task() = 0; virtual void init_regs() = 0; virtual void init_stack() = 0; @@ -178,35 +170,6 @@ protected: std::shared_ptr thread; public: - //u32 get_entry() const - //{ - // return thread->entry; - //} - virtual cpu_thread& args(std::initializer_list values) = 0; - virtual cpu_thread& run() = 0; - - //u64 join() - //{ - // if (!joinable()) - // throw EXCEPTION("thread must be joinable for join"); - - // thread->SetJoinable(false); - - // while (thread->IsRunning()) - // std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - - // return thread->GetExitStatus(); - //} - - //bool joinable() const - //{ - // return thread->IsJoinable(); - //} - - //u32 get_id() const - //{ - // return thread->GetId(); - //} }; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 03f039034b..2e677ecce4 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -263,7 +263,6 @@ RecompilationEngine::RecompilationEngine() RecompilationEngine::~RecompilationEngine() { m_executable_storage.clear(); - join(); memory_helper::free_reserved_memory(FunctionCache, VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType)); free(FunctionCachePagesCommited); } @@ -306,8 +305,8 @@ void RecompilationEngine::NotifyBlockStart(u32 address) { m_pending_address_start.push_back(address); } - if (!joinable()) { - start(WRAP_EXPR("PPU Recompilation Engine"), WRAP_EXPR(Task())); + if (!is_started()) { + start(); } cv.notify_one(); @@ -324,12 +323,12 @@ raw_fd_ostream & RecompilationEngine::Log() { return *m_log; } -void RecompilationEngine::Task() { +void RecompilationEngine::on_task() { std::chrono::nanoseconds idling_time(0); std::chrono::nanoseconds recompiling_time(0); auto start = std::chrono::high_resolution_clock::now(); - while (joinable() && !Emu.IsStopped()) { + while (!Emu.IsStopped()) { bool work_done_this_iteration = false; std::list m_current_execution_traces; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index c9094311ee..b66ff0a185 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -773,7 +773,7 @@ namespace ppu_recompiler_llvm { * It then builds them asynchroneously and update the executable mapping * using atomic based locks to avoid undefined behavior. **/ - class RecompilationEngine final : protected named_thread_t { + class RecompilationEngine final : public named_thread_t { friend class CPUHybridDecoderRecompiler; public: virtual ~RecompilationEngine() override; @@ -790,7 +790,9 @@ namespace ppu_recompiler_llvm { /// Log llvm::raw_fd_ostream & Log(); - void Task(); + std::string get_name() const override { return "PPU Recompilation Engine"; } + + void on_task() override; /// Get a pointer to the instance of this class static std::shared_ptr GetInstance(); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 7f4b758459..0b9ae73e0f 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -57,26 +57,22 @@ void ppu_decoder_cache_t::initialize(u32 addr, u32 size) } PPUThread::PPUThread(const std::string& name) - : CPUThread(CPU_THREAD_PPU, name, WRAP_EXPR(fmt::format("PPU[0x%x] Thread (%s)[0x%08x]", m_id, m_name.c_str(), PC))) + : CPUThread(CPU_THREAD_PPU, name) { InitRotateMask(); } PPUThread::~PPUThread() { - if (is_current()) - { - detach(); - } - else - { - join(); - } - close_stack(); ppu_free_tls(m_id); } +std::string PPUThread::get_name() const +{ + return fmt::format("PPU Thread[0x%x] (%s)[0x%08x]", m_id, CPUThread::get_name(), PC); +} + void PPUThread::dump_info() const { extern std::string get_ps3_function_name(u64 fid); @@ -239,7 +235,7 @@ void PPUThread::fast_call(u32 addr, u32 rtoc) try { - task(); + cpu_task(); } catch (CPUThreadReturn) { @@ -264,7 +260,7 @@ void PPUThread::fast_stop() m_state |= CPU_STATE_RETURN; } -void PPUThread::task() +void PPUThread::cpu_task() { SetHostRoundingMode(FPSCR_RN_NEAR); diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 611d60b34a..2c055ef1ee 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -543,11 +543,12 @@ public: PPUThread(const std::string& name); virtual ~PPUThread() override; + virtual std::string get_name() const override; virtual void dump_info() const override; virtual u32 get_pc() const override { return PC; } virtual u32 get_offset() const override { return 0; } virtual void do_run() override; - virtual void task() override; + virtual void cpu_task() override; virtual void init_regs() override; virtual void init_stack() override; diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index c7c844721e..5063ee63f0 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -10,20 +10,9 @@ thread_local spu_mfc_arg_t raw_spu_mfc[8] = {}; RawSPUThread::RawSPUThread(const std::string& name, u32 index) - : SPUThread(CPU_THREAD_RAW_SPU, name, COPY_EXPR(fmt::format("RawSPU[%d] Thread (0x%x)[0x%05x]", index, m_id, pc)), index, RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index) + : SPUThread(CPU_THREAD_RAW_SPU, name, index, RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index) { - if (!vm::falloc(offset, 0x40000)) - { - throw EXCEPTION("Failed to allocate RawSPU local storage"); - } -} - -RawSPUThread::~RawSPUThread() -{ - join(); - - // Deallocate Local Storage - vm::dealloc_verbose_nothrow(offset); + CHECK_ASSERTION(vm::falloc(offset, 0x40000) == offset); } bool RawSPUThread::read_reg(const u32 addr, u32& value) @@ -233,7 +222,7 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value) return false; } -void RawSPUThread::task() +void RawSPUThread::cpu_task() { // get next PC and SPU Interrupt status pc = npc.exchange(0); @@ -242,7 +231,7 @@ void RawSPUThread::task() pc &= 0x3fffc; - SPUThread::task(); + SPUThread::cpu_task(); // save next PC and current SPU Interrupt status npc = pc | ((ch_event_stat & SPU_EVENT_INTR_ENABLED) != 0); diff --git a/rpcs3/Emu/Cell/RawSPUThread.h b/rpcs3/Emu/Cell/RawSPUThread.h index fbd011aa41..32cf470052 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.h +++ b/rpcs3/Emu/Cell/RawSPUThread.h @@ -19,11 +19,10 @@ class RawSPUThread final : public SPUThread { public: RawSPUThread(const std::string& name, u32 index); - virtual ~RawSPUThread(); bool read_reg(const u32 addr, u32& value); bool write_reg(const u32 addr, const u32 value); private: - virtual void task() override; + virtual void cpu_task() override; }; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 7722052293..51b21b9c21 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -52,33 +52,25 @@ void spu_int_ctrl_t::clear(u64 ints) const spu_imm_table_t g_spu_imm; -SPUThread::SPUThread(CPUThreadType type, const std::string& name, std::function thread_name, u32 index, u32 offset) - : CPUThread(type, name, std::move(thread_name)) +SPUThread::SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset) + : CPUThread(type, name) , index(index) , offset(offset) { } SPUThread::SPUThread(const std::string& name, u32 index) - : CPUThread(CPU_THREAD_SPU, name, WRAP_EXPR(fmt::format("SPU[0x%x] Thread (%s)[0x%05x]", m_id, m_name.c_str(), pc))) + : CPUThread(CPU_THREAD_SPU, name) , index(index) , offset(vm::alloc(0x40000, vm::main)) { - if (!offset) - { - throw EXCEPTION("Failed to allocate SPU local storage"); - } + CHECK_ASSERTION(offset); } SPUThread::~SPUThread() { - if (m_type == CPU_THREAD_SPU) - { - join(); - - // Deallocate Local Storage - vm::dealloc_verbose_nothrow(offset, vm::main); - } + // Deallocate Local Storage + vm::dealloc_verbose_nothrow(offset); } bool SPUThread::is_paused() const @@ -99,12 +91,17 @@ bool SPUThread::is_paused() const return false; } +std::string SPUThread::get_name() const +{ + return fmt::format("%s[0x%x] Thread (%s)[0x%05x]", CPUThread::GetTypeString(), m_id, CPUThread::get_name(), pc); +} + void SPUThread::dump_info() const { CPUThread::dump_info(); } -void SPUThread::task() +void SPUThread::cpu_task() { std::fesetround(FE_TOWARDZERO); @@ -251,7 +248,7 @@ void SPUThread::fast_call(u32 ls_addr) try { - task(); + cpu_task(); } catch (CPUThreadReturn) { diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index e74ddcccb0..c16547f3d6 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -661,7 +661,7 @@ public: u32 recursion_level = 0; protected: - SPUThread(CPUThreadType type, const std::string& name, std::function thread_name, u32 index, u32 offset); + SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset); public: SPUThread(const std::string& name, u32 index); @@ -669,11 +669,12 @@ public: virtual bool is_paused() const override; + virtual std::string get_name() const override; virtual void dump_info() const override; virtual u32 get_pc() const override { return pc; } virtual u32 get_offset() const override { return offset; } virtual void do_run() override; - virtual void task() override; + virtual void cpu_task() override; virtual void init_regs() override; virtual void init_stack() override; diff --git a/rpcs3/Emu/IdManager.cpp b/rpcs3/Emu/IdManager.cpp index d9e2d6720d..d178be46a1 100644 --- a/rpcs3/Emu/IdManager.cpp +++ b/rpcs3/Emu/IdManager.cpp @@ -1,14 +1,157 @@ #include "stdafx.h" #include "IdManager.h" -std::mutex idm::g_mutex; +namespace idm +{ + std::mutex g_mutex; -std::unordered_map idm::g_map; + idm::map_t g_map; -u32 idm::g_last_raw_id = 0; + u32 g_last_raw_id = 0; -thread_local u32 idm::g_tls_last_id = 0xdeadbeef; + thread_local u32 g_tls_last_id = 0xdeadbeef; +} -std::mutex fxm::g_mutex; +namespace fxm +{ + std::mutex g_mutex; -std::unordered_map> fxm::g_map; + fxm::map_t g_map; +} + +void idm::clear() +{ + std::lock_guard lock{g_mutex}; + + // Call recorded id_finalize functions for all IDs + for (auto& id : idm::map_t(std::move(g_map))) + { + (*id.second.type_index)(id.second.data.get()); + } + + g_last_raw_id = 0; +} + +bool idm::check(u32 in_id, id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + const auto found = g_map.find(in_id); + + return found != g_map.end() && found->second.type_index == type; +} + +// check if ID exists and return its type or nullptr +const std::type_info* idm::get_type(u32 raw_id) +{ + std::lock_guard lock(g_mutex); + + const auto found = g_map.find(raw_id); + + return found == g_map.end() ? nullptr : found->second.info; +} + +std::shared_ptr idm::get(u32 in_id, id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + const auto found = g_map.find(in_id); + + if (found == g_map.end() || found->second.type_index != type) + { + return nullptr; + } + + return found->second.data; +} + +idm::map_t idm::get_all(id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + idm::map_t result; + + for (auto& id : g_map) + { + if (id.second.type_index == type) + { + result.insert(id); + } + } + + return result; +} + +std::shared_ptr idm::withdraw(u32 in_id, id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + const auto found = g_map.find(in_id); + + if (found == g_map.end() || found->second.type_index != type) + { + return nullptr; + } + + auto ptr = std::move(found->second.data); + + g_map.erase(found); + + return ptr; +} + +u32 idm::get_count(id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + u32 result = 0; + + for (auto& id : g_map) + { + if (id.second.type_index == type) + { + result++; + } + } + + return result; +} + + +void fxm::clear() +{ + std::lock_guard lock{g_mutex}; + + // Call recorded id_finalize functions for all IDs + for (auto& id : fxm::map_t(std::move(g_map))) + { + if (id.second) (*id.first)(id.second.get()); + } +} + +bool fxm::check(id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + const auto found = g_map.find(type); + + return found != g_map.end() && found->second; +} + +std::shared_ptr fxm::get(id_type_index_t type) +{ + std::lock_guard lock(g_mutex); + + const auto found = g_map.find(type); + + return found != g_map.end() ? found->second : nullptr; +} + +std::shared_ptr fxm::withdraw(id_type_index_t type) +{ + std::unique_lock lock(g_mutex); + + const auto found = g_map.find(type); + + return found != g_map.end() ? std::move(found->second) : nullptr; +} diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index a50dc7c51d..2bbcfeca04 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -2,40 +2,61 @@ #define ID_MANAGER_INCLUDED -template struct type_info_t { static char value; }; +// TODO: make id_aux_initialize and id_aux_finalize safer against a possible ODR violation -template char type_info_t::value = 42; - -template constexpr inline const void* get_type_index() +// Function called after the successfull creation of an ID (does nothing by default, provide an overload) +inline void id_aux_initialize(void*) { - return &type_info_t::value; + ; } -// default traits for any arbitrary type -template struct id_traits +// Function called after the ID removal (does nothing by default, provide an overload) +inline void id_aux_finalize(void*) { - // get next mapped id (may return 0 if out of IDs) - static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; } + ; +} - // convert "public" id to mapped id (may return 0 if invalid) - static u32 in_id(u32 id) { return id; } +// Type-erased id_aux_* function type +using id_aux_func_t = void(*)(void*); - // convert mapped id to "public" id - static u32 out_id(u32 raw_id) { return raw_id; } +template +struct id_type_info_t +{ + static const auto size = sizeof(T); // forbid forward declarations + + static const id_aux_func_t on_remove; }; -struct id_data_t final +// Type-erased finalization function +template +const id_aux_func_t id_type_info_t::on_remove = [](void* ptr) { - std::shared_ptr data; - const std::type_info* info; - const void* type_index; + return id_aux_finalize(static_cast(ptr)); +}; - template inline id_data_t(std::shared_ptr data) - : data(std::move(data)) - , info(&typeid(T)) - , type_index(get_type_index()) - { - } +using id_type_index_t = const id_aux_func_t*; + +// Get a unique pointer to the on_remove value (will be unique for each type) +template +inline constexpr id_type_index_t get_id_type_index() +{ + return &id_type_info_t::on_remove; +} + +// Default ID traits for any arbitrary type +template +struct id_traits +{ + static const auto size = sizeof(T); // forbid forward declarations + + // Get next mapped id (may return 0 if out of IDs) + static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; } + + // Convert "public" id to mapped id (may return 0 if invalid) + static u32 in_id(u32 id) { return id; } + + // Convert mapped id to "public" id + static u32 out_id(u32 raw_id) { return raw_id; } }; // ID Manager @@ -44,52 +65,55 @@ struct id_data_t final // 0x80000000+ : reserved (may be used through id_traits specializations) namespace idm { - extern std::mutex g_mutex; - - extern std::unordered_map g_map; - - extern u32 g_last_raw_id; - - thread_local extern u32 g_tls_last_id; - - // can be called from the constructor called through make() or make_ptr() to get the ID of the object being created - static inline u32 get_last_id() + struct id_data_t final { + std::shared_ptr data; + const std::type_info* info; + id_type_index_t type_index; + + template id_data_t(const std::shared_ptr& data) + : data(data) + , info(&typeid(T)) + , type_index(get_id_type_index()) + { + } + }; + + using map_t = std::unordered_map; + + // Can be called from the constructor called through make() or make_ptr() to get the ID of the object being created + inline u32 get_last_id() + { + extern thread_local u32 g_tls_last_id; + return g_tls_last_id; } - // reinitialize ID manager - static void clear() - { - std::lock_guard lock(g_mutex); + // Remove all objects + void clear(); - g_map.clear(); - g_last_raw_id = 0; + // Internal + bool check(u32 in_id, id_type_index_t type); + + // Check if an ID of specified type exists + template + bool check(u32 id) + { + return check(id_traits::in_id(id), get_id_type_index()); } - // check if ID of specified type exists - template static bool check(u32 id) + // Check if an ID exists and return its type or nullptr + const std::type_info* get_type(u32 raw_id); + + // Internal + template + std::shared_ptr add(Ptr&& get_ptr) { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(id_traits::in_id(id)); + extern std::mutex g_mutex; + extern idm::map_t g_map; + extern u32 g_last_raw_id; + extern thread_local u32 g_tls_last_id; - return found != g_map.end() && found->second.type_index == get_type_index(); - } - - // check if ID exists and return its type or nullptr - inline static const std::type_info* get_type(u32 raw_id) - { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(raw_id); - - return found == g_map.end() ? nullptr : found->second.info; - } - - // add new ID of specified type with specified constructor arguments (returns object or nullptr) - template static std::enable_if_t::value, std::shared_ptr> make_ptr(Args&&... args) - { std::lock_guard lock(g_mutex); for (u32 raw_id = g_last_raw_id; (raw_id = id_traits::next_id(raw_id)); /**/) @@ -98,7 +122,7 @@ namespace idm g_tls_last_id = id_traits::out_id(raw_id); - auto ptr = std::make_shared(std::forward(args)...); + std::shared_ptr ptr = get_ptr(); g_map.emplace(raw_id, id_data_t(ptr)); @@ -110,332 +134,302 @@ namespace idm return nullptr; } - // add new ID of specified type with specified constructor arguments (returns id) - template static std::enable_if_t::value, u32> make(Args&&... args) + // Add a new ID of specified type with specified constructor arguments (returns object or nullptr) + template + std::enable_if_t::value, std::shared_ptr> make_ptr(Args&&... args) { - std::lock_guard lock(g_mutex); - - for (u32 raw_id = g_last_raw_id; (raw_id = id_traits::next_id(raw_id)); /**/) + if (auto ptr = add(WRAP_EXPR(std::make_shared(std::forward(args)...)))) { - if (g_map.find(raw_id) != g_map.end()) continue; - - g_tls_last_id = id_traits::out_id(raw_id); - - g_map.emplace(raw_id, id_data_t(std::make_shared(std::forward(args)...))); - - if (raw_id < 0x80000000) g_last_raw_id = raw_id; - - return id_traits::out_id(raw_id); + id_aux_initialize(ptr.get()); + return ptr; } - throw EXCEPTION("Out of IDs"); + return nullptr; } - // add new ID for an existing object provided (don't use for initial object creation) - template static u32 import(const std::shared_ptr& ptr) + // Add a new ID of specified type with specified constructor arguments (returns id) + template + std::enable_if_t::value, u32> make(Args&&... args) { - std::lock_guard lock(g_mutex); - - for (u32 raw_id = g_last_raw_id; (raw_id = id_traits::next_id(raw_id)); /**/) + if (auto ptr = add(WRAP_EXPR(std::make_shared(std::forward(args)...)))) { - if (g_map.find(raw_id) != g_map.end()) continue; - - g_tls_last_id = id_traits::out_id(raw_id); - - g_map.emplace(raw_id, id_data_t(ptr)); - - if (raw_id < 0x80000000) g_last_raw_id = raw_id; - - return id_traits::out_id(raw_id); + id_aux_initialize(ptr.get()); + return get_last_id(); } - throw EXCEPTION("Out of IDs"); + throw EXCEPTION("Out of IDs ('%s')", typeid(T).name()); } - // get ID of specified type - template static std::shared_ptr get(u32 id) + // Add a new ID for an existing object provided (returns new id) + template + u32 import(const std::shared_ptr& ptr) { - std::lock_guard lock(g_mutex); + static const auto size = sizeof(T); // forbid forward declarations - const auto found = g_map.find(id_traits::in_id(id)); - - if (found == g_map.end() || found->second.type_index != get_type_index()) + if (add(WRAP_EXPR(ptr))) { - return nullptr; + id_aux_initialize(ptr.get()); + return get_last_id(); } - return std::static_pointer_cast(found->second.data); + throw EXCEPTION("Out of IDs ('%s')", typeid(T).name()); } - // get all IDs of specified type T (unsorted) - template static std::vector> get_all() - { - std::lock_guard lock(g_mutex); + // Internal + std::shared_ptr get(u32 in_id, id_type_index_t type); + // Get ID of specified type + template + std::shared_ptr get(u32 id) + { + return std::static_pointer_cast(get(id_traits::in_id(id), get_id_type_index())); + } + + // Internal + idm::map_t get_all(id_type_index_t type); + + // Get all IDs of specified type T (unsorted) + template + std::vector> get_all() + { std::vector> result; - const auto type = get_type_index(); - - for (auto& v : g_map) + for (auto& id : get_all(get_id_type_index())) { - if (v.second.type_index == type) - { - result.emplace_back(std::static_pointer_cast(v.second.data)); - } + result.emplace_back(std::static_pointer_cast(id.second.data)); } return result; } - // remove ID created with type T - template static bool remove(u32 id) + std::shared_ptr withdraw(u32 in_id, id_type_index_t type); + + // Remove the ID created with type T + template + bool remove(u32 id) { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(id_traits::in_id(id)); - - if (found == g_map.end() || found->second.type_index != get_type_index()) + if (auto ptr = withdraw(id_traits::in_id(id), get_id_type_index())) { - return false; + id_aux_finalize(static_cast(ptr.get())); + + return true; } - g_map.erase(found); - - return true; + return false; } - // remove ID created with type T and return the object - template static std::shared_ptr withdraw(u32 id) + // Remove the ID created with type T and return it + template + std::shared_ptr withdraw(u32 id) { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(id_traits::in_id(id)); - - if (found == g_map.end() || found->second.type_index != get_type_index()) + if (auto ptr = std::static_pointer_cast(withdraw(id_traits::in_id(id), get_id_type_index()))) { - return nullptr; + id_aux_finalize(ptr.get()); + + return ptr; } - auto ptr = std::static_pointer_cast(found->second.data); - - g_map.erase(found); - - return ptr; + return nullptr; } - template static u32 get_count() + u32 get_count(id_type_index_t type); + + template + u32 get_count() { - std::lock_guard lock(g_mutex); - - u32 result = 0; - - const auto type = get_type_index(); - - for (auto& v : g_map) - { - if (v.second.type_index == type) - { - result++; - } - } - - return result; + return get_count(get_id_type_index()); } - // get sorted ID list of specified type - template static std::set get_set() + // Get sorted list of all IDs of specified type + template + std::set get_set() { - std::lock_guard lock(g_mutex); - std::set result; - const auto type = get_type_index(); - - for (auto& v : g_map) + for (auto& id : get_all(get_id_type_index())) { - if (v.second.type_index == type) - { - result.insert(id_traits::out_id(v.first)); - } + result.emplace(id_traits::out_id(id.first)); } return result; } - // get sorted ID map (ID value -> ID data) of specified type - template static std::map> get_map() + // Get sorted map (ID value -> ID data) of all IDs of specified type + template + std::map> get_map() { - std::lock_guard lock(g_mutex); - std::map> result; - const auto type = get_type_index(); - - for (auto& v : g_map) + for (auto& id : get_all(get_id_type_index())) { - if (v.second.type_index == type) - { - result[id_traits::out_id(v.first)] = std::static_pointer_cast(v.second.data); - } + result[id_traits::out_id(id.first)] = std::static_pointer_cast(id.second.data); } return result; } -}; +} // Fixed Object Manager // allows to manage shared objects of any specified type, but only one object per type; // object are deleted when the emulation is stopped namespace fxm { - extern std::mutex g_mutex; + using map_t = std::unordered_map>; - extern std::unordered_map> g_map; + // Remove all objects + void clear(); - // reinitialize - static void clear() + // Internal (returns old and new pointers) + template + std::pair, std::shared_ptr> add(Ptr&& get_ptr) { + extern std::mutex g_mutex; + extern fxm::map_t g_map; + std::lock_guard lock(g_mutex); - g_map.clear(); + auto& item = g_map[get_id_type_index()]; + + if (Always || !item) + { + std::shared_ptr old = std::static_pointer_cast(std::move(item)); + std::shared_ptr ptr = get_ptr(); + + // Set new object + item = ptr; + + return{ std::move(old), std::move(ptr) }; + } + else + { + return{ std::static_pointer_cast(item), nullptr }; + } } - // add fixed object of specified type only if it doesn't exist (one unique object per type may exist) - template static std::enable_if_t::value, std::shared_ptr> make(Args&&... args) + // Create the object (returns nullptr if it already exists) + template + std::enable_if_t::value, std::shared_ptr> make(Args&&... args) { - std::lock_guard lock(g_mutex); + auto pair = add(WRAP_EXPR(std::make_shared(std::forward(args)...))); - const auto index = get_type_index(); - - const auto found = g_map.find(index); - - // only if object of this type doesn't exist - if (found == g_map.end()) + if (pair.second) { - auto ptr = std::make_shared(std::forward(args)...); - - g_map.emplace(index, ptr); - - return ptr; + id_aux_initialize(pair.second.get()); + return std::move(pair.second); } return nullptr; } - // add fixed object of specified type, replacing previous one if it exists - template static std::enable_if_t::value, std::shared_ptr> make_always(Args&&... args) + // Create the object unconditionally (old object will be removed if it exists) + template + std::enable_if_t::value, std::shared_ptr> make_always(Args&&... args) { - std::lock_guard lock(g_mutex); + auto pair = add(WRAP_EXPR(std::make_shared(std::forward(args)...))); - auto ptr = std::make_shared(std::forward(args)...); - - g_map[get_type_index()] = ptr; - - return ptr; - } - - // import existing fixed object of specified type only if it doesn't exist (don't use) - template static std::shared_ptr import(std::shared_ptr&& ptr) - { - std::lock_guard lock(g_mutex); - - const auto index = get_type_index(); - - const auto found = g_map.find(index); - - if (found == g_map.end()) + if (pair.first) { - g_map.emplace(index, ptr); - - return ptr; + id_aux_finalize(pair.first.get()); } + id_aux_initialize(pair.second.get()); + return std::move(pair.second); + } + + // Emplace the object + template + bool import(const std::shared_ptr& ptr) + { + static const auto size = sizeof(T); // forbid forward declarations + + auto pair = add(WRAP_EXPR(ptr)); + + if (pair.second) + { + id_aux_initialize(pair.second.get()); + return true; + } + + return false; + } + + // Emplace the object unconditionally (old object will be removed if it exists) + template + void import_always(const std::shared_ptr& ptr) + { + static const auto size = sizeof(T); // forbid forward declarations + + auto pair = add(WRAP_EXPR(ptr)); + + if (pair.first) + { + id_aux_finalize(pair.first.get()); + } + + id_aux_initialize(pair.second.get()); + } + + // Get the object unconditionally (create an object if it doesn't exist) + template + std::enable_if_t::value, std::shared_ptr> get_always(Args&&... args) + { + auto pair = add(WRAP_EXPR(std::make_shared(std::forward(args)...))); + + if (pair.second) + { + id_aux_initialize(pair.second.get()); + return std::move(pair.second); + } + + return std::move(pair.first); + } + + // Internal + bool check(id_type_index_t type); + + // Check whether the object exists + template + bool check() + { + return check(get_id_type_index()); + } + + // Internal + std::shared_ptr get(id_type_index_t type); + + // Get the object (returns nullptr if it doesn't exist) + template + std::shared_ptr get() + { + return std::static_pointer_cast(get(get_id_type_index())); + } + + // Internal + std::shared_ptr withdraw(id_type_index_t type); + + // Delete the object + template + bool remove() + { + if (auto ptr = withdraw(get_id_type_index())) + { + id_aux_finalize(static_cast(ptr.get())); + return true; + } + + return false; + } + + // Delete the object and return it + template + std::shared_ptr withdraw() + { + if (auto ptr = std::static_pointer_cast(withdraw(get_id_type_index()))) + { + id_aux_finalize(ptr.get()); + return ptr; + } + return nullptr; } - - // import existing fixed object of specified type, replacing previous one if it exists (don't use) - template static std::shared_ptr import_always(std::shared_ptr&& ptr) - { - std::lock_guard lock(g_mutex); - - g_map[get_type_index()] = ptr; - - return ptr; - } - - // get fixed object of specified type (always returns an object, it's created if it doesn't exist) - template static std::enable_if_t::value, std::shared_ptr> get_always(Args&&... args) - { - std::lock_guard lock(g_mutex); - - const auto index = get_type_index(); - - const auto found = g_map.find(index); - - if (found == g_map.end()) - { - auto ptr = std::make_shared(std::forward(args)...); - - g_map[index] = ptr; - - return ptr; - } - - return std::static_pointer_cast(found->second); - } - - // check whether the object exists - template static bool check() - { - std::lock_guard lock(g_mutex); - - return g_map.find(get_type_index()) != g_map.end(); - } - - // get fixed object of specified type (returns nullptr if it doesn't exist) - template static std::shared_ptr get() - { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(get_type_index()); - - if (found == g_map.end()) - { - return nullptr; - } - - return std::static_pointer_cast(found->second); - } - - // remove fixed object created with type T - template static bool remove() - { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(get_type_index()); - - if (found == g_map.end()) - { - return false; - } - - return g_map.erase(found), true; - } - - // remove fixed object created with type T and return it - template static std::shared_ptr withdraw() - { - std::lock_guard lock(g_mutex); - - const auto found = g_map.find(get_type_index()); - - if (found == g_map.end()) - { - return nullptr; - } - - auto ptr = std::static_pointer_cast(std::move(found->second)); - - return g_map.erase(found), ptr; - } -}; +} diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 88b67299b2..283e5bfe58 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -90,13 +90,13 @@ namespace vm std::vector> g_locations; // memory locations - const thread_ctrl_t* const INVALID_THREAD = reinterpret_cast(~0ull); - //using reservation_mutex_t = std::mutex; class reservation_mutex_t { - atomic_t m_owner{ INVALID_THREAD }; + std::atomic m_lock{ false }; + std::thread::id m_owner{}; + std::condition_variable m_cv; std::mutex m_mutex; @@ -105,13 +105,11 @@ namespace vm never_inline void lock() { - auto owner = get_current_thread_ctrl(); - std::unique_lock lock(m_mutex, std::defer_lock); - while (!m_owner.compare_and_swap_test(INVALID_THREAD, owner)) + while (m_lock.exchange(true) == true) { - if (m_owner == owner) + if (m_owner == std::this_thread::get_id()) { throw EXCEPTION("Deadlock"); } @@ -125,26 +123,33 @@ namespace vm m_cv.wait_for(lock, std::chrono::milliseconds(1)); } + m_owner = std::this_thread::get_id(); do_notify = true; } never_inline void unlock() { - auto owner = get_current_thread_ctrl(); + if (m_owner != std::this_thread::get_id()) + { + throw EXCEPTION("Mutex not owned"); + } - if (!m_owner.compare_and_swap_test(owner, INVALID_THREAD)) + m_owner = {}; + + if (m_lock.exchange(false) == false) { throw EXCEPTION("Lost lock"); } if (do_notify) { + std::lock_guard lock(m_mutex); m_cv.notify_one(); } } }; - const thread_ctrl_t* volatile g_reservation_owner = nullptr; + const thread_ctrl* volatile g_reservation_owner = nullptr; u32 g_reservation_addr = 0; u32 g_reservation_size = 0; @@ -352,7 +357,7 @@ namespace vm void start() { // start notification thread - named_thread_t(COPY_EXPR("vm::start thread"), []() + thread_ctrl::spawn(PURE_EXPR("vm::start thread"s), []() { while (!Emu.IsStopped()) { @@ -364,8 +369,7 @@ namespace vm std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - - }).detach(); + }); } void _reservation_set(u32 addr, bool no_access = false) @@ -436,9 +440,6 @@ namespace vm throw EXCEPTION("Invalid page flags (addr=0x%x, size=0x%x, flags=0x%x)", addr, size, flags); } - // silent unlocking to prevent priority boost for threads going to break reservation - //g_reservation_mutex.do_notify = false; - // break the reservation g_tls_did_break_reservation = g_reservation_owner && _reservation_break(g_reservation_addr); @@ -451,7 +452,7 @@ namespace vm // set additional information g_reservation_addr = addr; g_reservation_size = size; - g_reservation_owner = get_current_thread_ctrl(); + g_reservation_owner = thread_ctrl::get_current(); // copy data std::memcpy(data, vm::base(addr), size); @@ -468,7 +469,7 @@ namespace vm throw EXCEPTION("Invalid arguments (addr=0x%x, size=0x%x)", addr, size); } - if (g_reservation_owner != get_current_thread_ctrl() || g_reservation_addr != addr || g_reservation_size != size) + if (g_reservation_owner != thread_ctrl::get_current() || g_reservation_addr != addr || g_reservation_size != size) { // atomic update failed return false; @@ -522,7 +523,7 @@ namespace vm return true; } - bool reservation_test(const thread_ctrl_t* current) + bool reservation_test(const thread_ctrl* current) { const auto owner = g_reservation_owner; @@ -535,7 +536,7 @@ namespace vm { std::lock_guard lock(g_reservation_mutex); - if (g_reservation_owner && g_reservation_owner == get_current_thread_ctrl()) + if (g_reservation_owner && g_reservation_owner == thread_ctrl::get_current()) { g_tls_did_break_reservation = _reservation_break(g_reservation_addr); } @@ -556,7 +557,7 @@ namespace vm g_tls_did_break_reservation = false; // check and possibly break previous reservation - if (g_reservation_owner != get_current_thread_ctrl() || g_reservation_addr != addr || g_reservation_size != size) + if (g_reservation_owner != thread_ctrl::get_current() || g_reservation_addr != addr || g_reservation_size != size) { if (g_reservation_owner) { @@ -572,7 +573,7 @@ namespace vm // set additional information g_reservation_addr = addr; g_reservation_size = size; - g_reservation_owner = get_current_thread_ctrl(); + g_reservation_owner = thread_ctrl::get_current(); // may not be necessary _mm_mfence(); @@ -783,13 +784,13 @@ namespace vm if (!block) { - LOG_ERROR(MEMORY, "%s(): invalid memory location (%d, addr=0x%x)\n", __func__, location, addr); + LOG_ERROR(MEMORY, "vm::dealloc(): invalid memory location (%d, addr=0x%x)\n", location, addr); return; } if (!block->dealloc(addr)) { - LOG_ERROR(MEMORY, "%s(): deallocation failed (addr=0x%x)\n", __func__, addr); + LOG_ERROR(MEMORY, "vm::dealloc(): deallocation failed (addr=0x%x)\n", addr); return; } } diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index fbc54a7315..dce9f8a07b 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -1,9 +1,6 @@ #pragma once - -const class thread_ctrl_t* get_current_thread_ctrl(); - -class named_thread_t; +#include "Utilities/Thread.h" namespace vm { @@ -122,7 +119,7 @@ namespace vm bool reservation_query(u32 addr, u32 size, bool is_writing, std::function callback); // Returns true if the current thread owns reservation - bool reservation_test(const thread_ctrl_t* current = get_current_thread_ctrl()); + bool reservation_test(const thread_ctrl* current = thread_ctrl::get_current()); // Break all reservations created by the current thread void reservation_free(); diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp index eb772ca656..35eb941b55 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp @@ -232,11 +232,11 @@ D3D12GSRender::~D3D12GSRender() release_d2d_structures(); } -void D3D12GSRender::onexit_thread() +void D3D12GSRender::on_exit() { } -bool D3D12GSRender::domethod(u32 cmd, u32 arg) +bool D3D12GSRender::do_method(u32 cmd, u32 arg) { switch (cmd) { diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h index 614472f346..64735f818c 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h @@ -205,8 +205,8 @@ private: void copy_render_target_to_dma_location(); protected: - virtual void onexit_thread() override; - virtual bool domethod(u32 cmd, u32 arg) override; + virtual void on_exit() override; + virtual bool do_method(u32 cmd, u32 arg) override; virtual void end() override; virtual void flip(int buffer) override; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index fbbef5a43b..7ef5fc0099 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1046,9 +1046,9 @@ void GLGSRender::end() rsx::thread::end(); } -void GLGSRender::oninit_thread() +void GLGSRender::on_init_thread() { - GSRender::oninit_thread(); + GSRender::on_init_thread(); gl::init(); LOG_NOTICE(Log::RSX, (const char*)glGetString(GL_VERSION)); @@ -1071,7 +1071,7 @@ void GLGSRender::oninit_thread() m_vao.element_array_buffer = m_ebo; } -void GLGSRender::onexit_thread() +void GLGSRender::on_exit() { glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); @@ -1184,7 +1184,7 @@ static const std::unordered_map g_gl_method_tbl = { NV4097_CLEAR_SURFACE, nv4097_clear_surface } }; -bool GLGSRender::domethod(u32 cmd, u32 arg) +bool GLGSRender::do_method(u32 cmd, u32 arg) { auto found = g_gl_method_tbl.find(cmd); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 705a0d1d26..06ffd034c6 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -100,9 +100,9 @@ protected: void begin() override; void end() override; - void oninit_thread() override; - void onexit_thread() override; - bool domethod(u32 id, u32 arg) override; + void on_init_thread() override; + void on_exit() override; + bool do_method(u32 id, u32 arg) override; void flip(int buffer) override; u64 timestamp() const override; }; diff --git a/rpcs3/Emu/RSX/GSManager.cpp b/rpcs3/Emu/RSX/GSManager.cpp index d95d1eabe1..dd9002bf89 100644 --- a/rpcs3/Emu/RSX/GSManager.cpp +++ b/rpcs3/Emu/RSX/GSManager.cpp @@ -22,23 +22,19 @@ void GSInfo::Init() mode.pitch = 4; } -GSManager::GSManager() : m_render(nullptr) -{ -} - void GSManager::Init() { - if(m_render) return; + if (m_render) return; m_info.Init(); switch (rpcs3::state.config.rsx.renderer.value()) { default: - case rsx_renderer_type::Null : m_render = new NullGSRender(); break; - case rsx_renderer_type::OpenGL: m_render = new GLGSRender(); break; + case rsx_renderer_type::Null : m_render = std::make_shared(); break; + case rsx_renderer_type::OpenGL: m_render = std::make_shared(); break; #ifdef _MSC_VER - case rsx_renderer_type::DX12: m_render = new D3D12GSRender(); break; + case rsx_renderer_type::DX12: m_render = std::make_shared(); break; #endif } @@ -47,12 +43,7 @@ void GSManager::Init() void GSManager::Close() { - if(m_render) - { - m_render->close(); - delete m_render; - m_render = nullptr; - } + m_render.reset(); } u8 GSManager::GetState() diff --git a/rpcs3/Emu/RSX/GSManager.h b/rpcs3/Emu/RSX/GSManager.h index fc6d73e97b..91f9ae888b 100644 --- a/rpcs3/Emu/RSX/GSManager.h +++ b/rpcs3/Emu/RSX/GSManager.h @@ -25,18 +25,16 @@ struct GSInfo class GSManager { GSInfo m_info; - GSRender* m_render; + std::shared_ptr m_render; public: - GSManager(); - void Init(); void Close(); bool IsInited() const { return m_render != nullptr; } GSInfo& GetInfo() { return m_info; } - GSRender& GetRender() { assert(m_render); return *m_render; } + GSRender& GetRender() { return *m_render; } u8 GetState(); u8 GetColorSpace(); diff --git a/rpcs3/Emu/RSX/GSRender.cpp b/rpcs3/Emu/RSX/GSRender.cpp index b4a71b1719..d7358fc642 100644 --- a/rpcs3/Emu/RSX/GSRender.cpp +++ b/rpcs3/Emu/RSX/GSRender.cpp @@ -31,11 +31,12 @@ GSRender::~GSRender() if (m_frame) { + m_frame->hide(); m_frame->close(); } } -void GSRender::oninit() +void GSRender::on_init() { if (m_frame) { @@ -43,7 +44,7 @@ void GSRender::oninit() } } -void GSRender::oninit_thread() +void GSRender::on_init_thread() { if (m_frame) { @@ -52,21 +53,10 @@ void GSRender::oninit_thread() } } -void GSRender::close() -{ - if (m_frame && m_frame->shown()) - { - m_frame->hide(); - } - - if (joinable()) - { - join(); - } -} - void GSRender::flip(int buffer) { if (m_frame) + { m_frame->flip(m_context); + } } diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index c3c6acc5b3..d82048d8b9 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -49,9 +49,8 @@ public: GSRender(frame_type type); virtual ~GSRender(); - void oninit() override; - void oninit_thread() override; + void on_init() override; + void on_init_thread() override; - void close(); void flip(int buffer) override; }; diff --git a/rpcs3/Emu/RSX/Null/NullGSRender.cpp b/rpcs3/Emu/RSX/Null/NullGSRender.cpp index efd06ae11f..29827b0ba6 100644 --- a/rpcs3/Emu/RSX/Null/NullGSRender.cpp +++ b/rpcs3/Emu/RSX/Null/NullGSRender.cpp @@ -6,11 +6,7 @@ NullGSRender::NullGSRender() : GSRender(frame_type::Null) { } -void NullGSRender::onexit_thread() -{ -} - -bool NullGSRender::domethod(u32 cmd, u32 value) +bool NullGSRender::do_method(u32 cmd, u32 value) { return false; } diff --git a/rpcs3/Emu/RSX/Null/NullGSRender.h b/rpcs3/Emu/RSX/Null/NullGSRender.h index af1602a6c0..34adb02ada 100644 --- a/rpcs3/Emu/RSX/Null/NullGSRender.h +++ b/rpcs3/Emu/RSX/Null/NullGSRender.h @@ -7,6 +7,5 @@ public: NullGSRender(); private: - void onexit_thread() override; - bool domethod(u32 cmd, u32 value) override; + bool do_method(u32 cmd, u32 value) override; }; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index c8a39e3060..5bc710b9a7 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -689,7 +689,7 @@ namespace rsx static void wrapper(thread *rsx, u32 arg) { // try process using gpu - if (rsx->domethod(id, arg)) + if (rsx->do_method(id, arg)) { if (rsx->capture_current_frame && id == NV4097_CLEAR_SURFACE) rsx->capture_frame("clear"); @@ -974,25 +974,23 @@ namespace rsx capture_frame("Draw " + std::to_string(vertex_draw_count)); } - void thread::task() + void thread::on_task() { - u8 inc; - LOG_NOTICE(RSX, "RSX thread started"); + on_init_thread(); - oninit_thread(); + reset(); last_flip_time = get_system_time() - 1000000; - autojoin_thread_t vblank(WRAP_EXPR("VBlank Thread"), [this]() + scope_thread_t vblank(PURE_EXPR("VBlank Thread"s), [this]() { const u64 start_time = get_system_time(); vblank_count = 0; - while (joinable()) + // TODO: exit condition + while (!Emu.IsStopped()) { - CHECK_EMU_STATUS; - if (get_system_time() - start_time > vblank_count * 1000000 / 60) { vblank_count++; @@ -1012,107 +1010,87 @@ namespace rsx } }); - reset(); - - try + // TODO: exit condition + while (true) { - while (joinable()) + CHECK_EMU_STATUS; + + be_t get = ctrl->get; + be_t put = ctrl->put; + + if (put == get || !Emu.IsRunning()) { - //TODO: async mode - if (Emu.IsStopped()) - { - LOG_WARNING(RSX, "RSX thread aborted"); - break; - } - - inc = 1; - - be_t get = ctrl->get; - be_t put = ctrl->put; - - if (put == get || !Emu.IsRunning()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - continue; - } - - const u32 cmd = ReadIO32(get); - const u32 count = (cmd >> 18) & 0x7ff; - - if (cmd & CELL_GCM_METHOD_FLAG_JUMP) - { - u32 offs = cmd & 0x1fffffff; - //LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put); - ctrl->get = offs; - continue; - } - if (cmd & CELL_GCM_METHOD_FLAG_CALL) - { - m_call_stack.push(get + 4); - u32 offs = cmd & ~3; - //LOG_WARNING(RSX, "rsx call(0x%x) #0x%x - 0x%x", offs, cmd, get); - ctrl->get = offs; - continue; - } - if (cmd == CELL_GCM_METHOD_FLAG_RETURN) - { - u32 get = m_call_stack.top(); - m_call_stack.pop(); - //LOG_WARNING(RSX, "rsx return(0x%x)", get); - ctrl->get = get; - continue; - } - if (cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) - { - //LOG_WARNING(RSX, "rsx non increment cmd! 0x%x", cmd); - inc = 0; - } - - if (cmd == 0) //nop - { - ctrl->get = get + 4; - continue; - } - - auto args = vm::ptr::make((u32)RSXIOMem.RealAddr(get + 4)); - - u32 first_cmd = (cmd & 0xffff) >> 2; - - if (cmd & 0x3) - { - LOG_WARNING(Log::RSX, "unaligned command: %s (0x%x from 0x%x)", get_method_name(first_cmd).c_str(), first_cmd, cmd & 0xffff); - } - - for (u32 i = 0; i < count; i++) - { - u32 reg = first_cmd + (i * inc); - u32 value = args[i]; - - if (rpcs3::config.misc.log.rsx_logging.value()) - { - LOG_NOTICE(Log::RSX, "%s(0x%x) = 0x%x", get_method_name(reg).c_str(), reg, value); - } - - method_registers[reg] = value; - if (capture_current_frame) - frame_debug.command_queue.push_back(std::make_pair(reg, value)); - - if (auto method = methods[reg]) - method(this, value); - } - - ctrl->get = get + (count + 1) * 4; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack + continue; } - } - catch (const std::exception& ex) - { - LOG_ERROR(Log::RSX, ex.what()); - throw; - } - LOG_NOTICE(RSX, "RSX thread ended"); + const u32 cmd = ReadIO32(get); + const u32 count = (cmd >> 18) & 0x7ff; - onexit_thread(); + if (cmd & CELL_GCM_METHOD_FLAG_JUMP) + { + u32 offs = cmd & 0x1fffffff; + //LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put); + ctrl->get = offs; + continue; + } + if (cmd & CELL_GCM_METHOD_FLAG_CALL) + { + m_call_stack.push(get + 4); + u32 offs = cmd & ~3; + //LOG_WARNING(RSX, "rsx call(0x%x) #0x%x - 0x%x", offs, cmd, get); + ctrl->get = offs; + continue; + } + if (cmd == CELL_GCM_METHOD_FLAG_RETURN) + { + u32 get = m_call_stack.top(); + m_call_stack.pop(); + //LOG_WARNING(RSX, "rsx return(0x%x)", get); + ctrl->get = get; + continue; + } + + if (cmd == 0) //nop + { + ctrl->get = get + 4; + continue; + } + + auto args = vm::ptr::make((u32)RSXIOMem.RealAddr(get + 4)); + + u32 first_cmd = (cmd & 0xffff) >> 2; + + if (cmd & 0x3) + { + LOG_WARNING(Log::RSX, "unaligned command: %s (0x%x from 0x%x)", get_method_name(first_cmd).c_str(), first_cmd, cmd & 0xffff); + } + + for (u32 i = 0; i < count; i++) + { + u32 reg = cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT ? first_cmd : first_cmd + i; + u32 value = args[i]; + + if (rpcs3::config.misc.log.rsx_logging.value()) + { + LOG_NOTICE(Log::RSX, "%s(0x%x) = 0x%x", get_method_name(reg).c_str(), reg, value); + } + + method_registers[reg] = value; + if (capture_current_frame) + frame_debug.command_queue.push_back(std::make_pair(reg, value)); + + if (auto method = methods[reg]) + method(this, value); + } + + ctrl->get = get + (count + 1) * 4; + } + } + + std::string thread::get_name() const + { + return "rsx::thread"s; } void thread::fill_scale_offset_data(void *buffer, bool is_d3d) const noexcept @@ -1240,8 +1218,8 @@ namespace rsx m_used_gcm_commands.clear(); - oninit(); - named_thread_t::start(WRAP_EXPR("rsx::thread"), WRAP_EXPR(task())); + on_init(); + start(); } u32 thread::ReadIO32(u32 addr) diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index c65e9133cb..99633dcb0b 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -255,7 +255,7 @@ namespace rsx } }; - class thread : protected named_thread_t + class thread : public named_thread_t { protected: std::stack m_call_stack; @@ -327,19 +327,20 @@ namespace rsx protected: virtual ~thread() {} + virtual void on_task() override; + public: + virtual std::string get_name() const override; + virtual void begin(); virtual void end(); - virtual void oninit() = 0; - virtual void oninit_thread() = 0; - virtual void onexit_thread() = 0; - virtual bool domethod(u32 cmd, u32 value) { return false; } + virtual void on_init() = 0; + virtual void on_init_thread() = 0; + virtual bool do_method(u32 cmd, u32 value) { return false; } virtual void flip(int buffer) = 0; virtual u64 timestamp() const; - void task(); - /** * Fill buffer with 4x4 scale offset matrix. * Vertex shader's position is to be multiplied by this matrix. diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp index 0eeb42cd53..629f2e01ee 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp @@ -20,6 +20,8 @@ extern u64 get_system_time(); AudioConfig g_audio; +std::shared_ptr g_audio_thread; + s32 cellAudioInit() { cellAudio.Warning("cellAudioInit()"); @@ -48,17 +50,9 @@ s32 cellAudioInit() std::memset(vm::base(g_audio.buffer), 0, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT); std::memset(vm::base(g_audio.indexes), 0, sizeof32(u64) * AUDIO_PORT_COUNT); - // check thread status - if (g_audio.thread.joinable()) - { - g_audio.thread.join(); - } - // start audio thread - g_audio.thread.start(WRAP_EXPR("Audio Thread"), []() + g_audio_thread = thread_ctrl::spawn(PURE_EXPR("Audio Thread"s), []() { - std::unique_lock lock(g_audio.thread.mutex); - const bool do_dump = rpcs3::config.audio.dump_to_file.value(); AudioDumper m_dump; @@ -81,9 +75,9 @@ s32 cellAudioInit() squeue_t out_queue; - autojoin_thread_t iat(WRAP_EXPR("Internal Audio Thread"), [&out_queue]() + scope_thread_t iat(PURE_EXPR("Internal Audio Thread"s), [&out_queue]() { - const bool use_u16 = rpcs3::config.audio.convert_to_u16.value();; + const bool use_u16 = rpcs3::config.audio.convert_to_u16.value(); Emu.GetAudioManager().GetAudioOut().Init(); @@ -141,7 +135,7 @@ s32 cellAudioInit() { if (Emu.IsPaused()) { - g_audio.thread.cv.wait_for(lock, std::chrono::milliseconds(1)); + std::this_thread::sleep_for(1ms); // hack continue; } @@ -155,7 +149,7 @@ s32 cellAudioInit() const u64 expected_time = g_audio.counter * AUDIO_SAMPLES * 1000000 / 48000; if (expected_time >= time_pos) { - g_audio.thread.cv.wait_for(lock, std::chrono::milliseconds(1)); + std::this_thread::sleep_for(1ms); // hack continue; } @@ -368,6 +362,8 @@ s32 cellAudioInit() LV2_LOCK; + std::lock_guard lock(g_audio.mutex); + for (auto key : g_audio.keys) { if (const auto queue = Emu.GetEventManager().GetEventQueue(key)) @@ -419,8 +415,10 @@ s32 cellAudioQuit() return CELL_AUDIO_ERROR_NOT_INIT; } - g_audio.thread.join(); + g_audio_thread->join(); + g_audio_thread.reset(); g_audio.state.exchange(AUDIO_STATE_NOT_INITIALIZED); + return CELL_OK; } @@ -653,8 +651,6 @@ s32 cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr stamp) // TODO: check tag (CELL_AUDIO_ERROR_TAG_NOT_FOUND error) - std::lock_guard lock(g_audio.thread.mutex); - *stamp = g_audio.start_time + Emu.GetPauseTime() + (port.counter + (tag - port.tag)) * 256000000 / 48000; return CELL_OK; @@ -686,8 +682,6 @@ s32 cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, vm::ptr tag) return CELL_AUDIO_ERROR_PARAM; } - std::lock_guard lock(g_audio.thread.mutex); - u64 tag_base = port.tag; if (tag_base % port.block > blockNo) { @@ -780,7 +774,7 @@ s32 cellAudioSetNotifyEventQueue(u64 key) return CELL_AUDIO_ERROR_NOT_INIT; } - std::lock_guard lock(g_audio.thread.mutex); + std::lock_guard lock(g_audio.mutex); for (auto k : g_audio.keys) // check for duplicates { @@ -813,7 +807,7 @@ s32 cellAudioRemoveNotifyEventQueue(u64 key) return CELL_AUDIO_ERROR_NOT_INIT; } - std::lock_guard lock(g_audio.thread.mutex); + std::lock_guard lock(g_audio.mutex); for (auto i = g_audio.keys.begin(); i != g_audio.keys.end(); i++) { diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.h b/rpcs3/Emu/SysCalls/Modules/cellAudio.h index 5227f20b39..dd184d6e2a 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.h +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.h @@ -99,9 +99,10 @@ enum AudioPortState : u32 struct AudioPortConfig { - std::mutex mutex; atomic_t state; + std::mutex mutex; + u32 channel; u32 block; u64 attr; @@ -124,7 +125,8 @@ struct AudioPortConfig struct AudioConfig final // custom structure { atomic_t state; - named_thread_t thread; + + std::mutex mutex; AudioPortConfig ports[AUDIO_PORT_COUNT]; u32 buffer; // 1 MB memory for audio ports @@ -133,16 +135,6 @@ struct AudioConfig final // custom structure u64 start_time; std::vector keys; - AudioConfig() = default; - - ~AudioConfig() - { - if (thread.joinable()) - { - thread.join(); - } - } - u32 open_port() { for (u32 i = 0; i < AUDIO_PORT_COUNT; i++) diff --git a/rpcs3/Emu/SysCalls/Modules/cellFs.cpp b/rpcs3/Emu/SysCalls/Modules/cellFs.cpp index e50abb4ba4..f6d9cff989 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellFs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellFs.cpp @@ -494,7 +494,7 @@ s32 cellFsStReadStart(u32 fd, u64 offset, u64 size) file->st_read_size = size; - file->st_thread.start(COPY_EXPR(fmt::format("FS ST Thread[0x%x]", fd)), [=]() + file->st_thread = thread_ctrl::spawn(PURE_EXPR("FS ST Thread"s), [=]() { std::unique_lock lock(file->mutex); @@ -572,7 +572,7 @@ s32 cellFsStReadStop(u32 fd) } file->cv.notify_all(); - file->st_thread.join(); + file->st_thread->join(); return CELL_OK; } @@ -937,7 +937,7 @@ s32 cellFsAioRead(vm::ptr aio, vm::ptr id, fs_aio_cb_t func) const s32 xid = (*id = ++g_fs_aio_id); - named_thread_t(WRAP_EXPR("FS AIO Read Thread"), [=]{ fsAio(aio, false, xid, func); }).detach(); + thread_ctrl::spawn(PURE_EXPR("FS AIO Read Thread"s), COPY_EXPR(fsAio(aio, false, xid, func))); return CELL_OK; } @@ -950,7 +950,7 @@ s32 cellFsAioWrite(vm::ptr aio, vm::ptr id, fs_aio_cb_t func) const s32 xid = (*id = ++g_fs_aio_id); - named_thread_t(WRAP_EXPR("FS AIO Write Thread"), [=]{ fsAio(aio, true, xid, func); }).detach(); + thread_ctrl::spawn(PURE_EXPR("FS AIO Write Thread"s), COPY_EXPR(fsAio(aio, true, xid, func))); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp b/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp index 4ef3ed4cfe..3d9e54c750 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp @@ -61,9 +61,9 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr msgString, vm::ptr(Emu.GetCallbacks().get_msg_dialog()); + const std::shared_ptr dlg(Emu.GetCallbacks().get_msg_dialog()); - if (!dlg) + if (!fxm::import(dlg)) { return CELL_SYSUTIL_ERROR_BUSY; } @@ -206,18 +206,17 @@ s32 cellMsgDialogClose(f32 delay) const u64 wait_until = get_system_time() + static_cast(std::max(delay, 0.0f) * 1000); - named_thread_t(WRAP_EXPR("MsgDialog Thread"), [=]() + thread_ctrl::spawn(PURE_EXPR("MsgDialog Thread"s), [=]() { while (dlg->state == MsgDialogState::Open && get_system_time() < wait_until) { - CHECK_EMU_STATUS; + if (Emu.IsStopped()) return; std::this_thread::sleep_for(1ms); } dlg->on_close(CELL_MSGDIALOG_BUTTON_NONE); - - }).detach(); + }); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/sys_net.cpp b/rpcs3/Emu/SysCalls/Modules/sys_net.cpp index beef3d7ef3..d79cfb719f 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_net.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_net.cpp @@ -128,7 +128,7 @@ namespace sys_net { g_tls_net_data.set(vm::alloc(sizeof(decltype(g_tls_net_data)::type), vm::main)); - current_thread_register_atexit([addr = g_tls_net_data.addr()] + thread_ctrl::at_exit([addr = g_tls_net_data.addr()] { vm::dealloc_verbose_nothrow(addr, vm::main); }); diff --git a/rpcs3/Emu/SysCalls/Modules/sys_ppu_thread_.cpp b/rpcs3/Emu/SysCalls/Modules/sys_ppu_thread_.cpp index b1c0765e22..4c29cac68f 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_ppu_thread_.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_ppu_thread_.cpp @@ -43,8 +43,14 @@ void sys_ppu_thread_exit(PPUThread& ppu, u64 val) // (deallocate TLS) // ... - // call the syscall - _sys_ppu_thread_exit(ppu, val); + if (ppu.hle_code == 0xaff080a4) + { + // Change sys_ppu_thread_exit code to the syscall code + ppu.hle_code = ~41; + } + + // Call the syscall + return _sys_ppu_thread_exit(ppu, val); } std::mutex g_once_mutex; diff --git a/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp b/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp index 6a0ccc7219..e1f08b89a2 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp @@ -215,7 +215,7 @@ s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout) // try to reown mutex and exit if timed out if (!cond->mutex->owner) { - cond->mutex->owner = ppu.shared_from_this(); + cond->mutex->owner = std::static_pointer_cast(ppu.shared_from_this()); break; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_fs.h b/rpcs3/Emu/SysCalls/lv2/sys_fs.h index facd26ce45..324958a54a 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_fs.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_fs.h @@ -184,7 +184,7 @@ struct lv2_file_t u64 st_trans_rate; bool st_copyless; - named_thread_t st_thread; + std::shared_ptr st_thread; u32 st_buffer; u64 st_read_size; diff --git a/rpcs3/Emu/SysCalls/lv2/sys_mutex.cpp b/rpcs3/Emu/SysCalls/lv2/sys_mutex.cpp index 0545db9b4e..8d7126285e 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_mutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_mutex.cpp @@ -132,7 +132,7 @@ s32 sys_mutex_lock(PPUThread& ppu, u32 mutex_id, u64 timeout) // lock immediately if not locked if (!mutex->owner) { - mutex->owner = ppu.shared_from_this(); + mutex->owner = std::static_pointer_cast(ppu.shared_from_this()); return CELL_OK; } @@ -207,7 +207,7 @@ s32 sys_mutex_trylock(PPUThread& ppu, u32 mutex_id) } // own the mutex if free - mutex->owner = ppu.shared_from_this(); + mutex->owner = std::static_pointer_cast(ppu.shared_from_this()); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp index bd11c50fdb..6c1cc29991 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp @@ -26,14 +26,20 @@ void _sys_ppu_thread_exit(PPUThread& ppu, u64 errorcode) } } - const auto thread = ppu.shared_from_this(); - if (!ppu.is_joinable) { idm::remove(ppu.get_id()); } + else + { + ppu.exit(); + } - ppu.exit(); + // Throw if this syscall was not called directly by the SC instruction + if (~ppu.hle_code != 41) + { + throw CPUThreadExit{}; + } } void sys_ppu_thread_yield() diff --git a/rpcs3/Emu/SysCalls/lv2/sys_rwlock.cpp b/rpcs3/Emu/SysCalls/lv2/sys_rwlock.cpp index b9ccab39e7..c2d6659fdf 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_rwlock.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_rwlock.cpp @@ -228,7 +228,7 @@ s32 sys_rwlock_wlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout) if (!rwlock->readers && !rwlock->writer) { - rwlock->writer = ppu.shared_from_this(); + rwlock->writer = std::static_pointer_cast(ppu.shared_from_this()); return CELL_OK; } @@ -300,7 +300,7 @@ s32 sys_rwlock_trywlock(PPUThread& ppu, u32 rw_lock_id) return CELL_EBUSY; } - rwlock->writer = ppu.shared_from_this(); + rwlock->writer = std::static_pointer_cast(ppu.shared_from_this()); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_timer.cpp b/rpcs3/Emu/SysCalls/lv2/sys_timer.cpp index 7dd48c3bcb..dea05baa2a 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_timer.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_timer.cpp @@ -13,55 +13,55 @@ SysCallBase sys_timer("sys_timer"); extern u64 get_system_time(); -lv2_timer_t::lv2_timer_t() - : start(0) - , period(0) - , state(SYS_TIMER_STATE_STOP) +void lv2_timer_t::on_task() { - auto name = fmt::format("Timer[0x%x] Thread", idm::get_last_id()); + std::unique_lock lock(mutex); - thread.start([name]{ return name; }, [this]() + while (state <= SYS_TIMER_STATE_RUN) { - std::unique_lock lock(thread.mutex); + CHECK_EMU_STATUS; - while (thread.joinable()) + if (state == SYS_TIMER_STATE_RUN) { - CHECK_EMU_STATUS; + if (lock) lock.unlock(); - if (state == SYS_TIMER_STATE_RUN) + LV2_LOCK; + + if (get_system_time() >= expire) { - LV2_LOCK; + const auto queue = port.lock(); - if (get_system_time() >= start) + if (queue) { - const auto queue = port.lock(); + queue->push(lv2_lock, source, data1, data2, expire); + } - if (queue) - { - queue->push(lv2_lock, source, data1, data2, start); - } + if (period && queue) + { + expire += period; // set next expiration time - if (period && queue) - { - start += period; // set next expiration time - - continue; // hack: check again - } - else - { - state = SYS_TIMER_STATE_STOP; // stop if oneshot or the event port was disconnected (TODO: is it correct?) - } + continue; // hack: check again + } + else + { + state = SYS_TIMER_STATE_STOP; // stop if oneshot or the event port was disconnected (TODO: is it correct?) } } - - thread.cv.wait_for(lock, std::chrono::milliseconds(1)); } - }); + + if (!lock) + { + lock.lock(); + continue; + } + + cv.wait_for(lock, std::chrono::milliseconds(1)); + } } -lv2_timer_t::~lv2_timer_t() +lv2_timer_t::lv2_timer_t() + : id(idm::get_last_id()) { - thread.join(); } s32 sys_timer_create(vm::ptr timer_id) @@ -109,7 +109,7 @@ s32 sys_timer_get_information(u32 timer_id, vm::ptr inf return CELL_ESRCH; } - info->next_expiration_time = timer->start; + info->next_expiration_time = timer->expire; info->period = timer->period; info->timer_state = timer->state; @@ -163,11 +163,14 @@ s32 _sys_timer_start(u32 timer_id, u64 base_time, u64 period) // sys_timer_start_periodic() will use current time (TODO: is it correct?) - timer->start = base_time ? base_time : start_time + period; + // lock for reliable notification + std::lock_guard lock(timer->mutex); + + timer->expire = base_time ? base_time : start_time + period; timer->period = period; timer->state = SYS_TIMER_STATE_RUN; - timer->thread.cv.notify_one(); + timer->cv.notify_one(); return CELL_OK; } @@ -196,8 +199,8 @@ s32 sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data LV2_LOCK; - const auto timer = idm::get(timer_id); - const auto queue = idm::get(queue_id); + const auto timer(idm::get(timer_id)); + const auto queue(idm::get(queue_id)); if (!timer || !queue) { diff --git a/rpcs3/Emu/SysCalls/lv2/sys_timer.h b/rpcs3/Emu/SysCalls/lv2/sys_timer.h index 90b4207aeb..5d1e32c68c 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_timer.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_timer.h @@ -19,22 +19,35 @@ struct sys_timer_information_t be_t pad; }; -struct lv2_timer_t final +class lv2_timer_t final : public named_thread_t { + void on_task() override; + + void on_id_aux_finalize() override + { + // Signal thread using invalid state and join + { std::lock_guard{mutex}, state = -1; } + + cv.notify_one(); + join(); + } + +public: + lv2_timer_t(); + + std::string get_name() const override { return fmt::format("Timer Thread[0x%x]", id); } + + const u32 id; + std::weak_ptr port; // event queue u64 source; // event source u64 data1; // event arg 1 u64 data2; // event arg 2 - u64 start; // next expiration time - u64 period; // period (oneshot if 0) + u64 expire = 0; // next expiration time + u64 period = 0; // period (oneshot if 0) - std::atomic state; // timer state - - named_thread_t thread; // timer thread - - lv2_timer_t(); - ~lv2_timer_t(); + std::atomic state{ SYS_TIMER_STATE_RUN }; // timer state }; s32 sys_timer_create(vm::ptr timer_id); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 3fb8991dc5..f234bade39 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -125,19 +125,28 @@ public: m_cb.send_dbg_command(cmd, thread); } - // returns a future object associated with the result of the function called from the GUI thread - template> inline std::future CallAfter(F&& func) const + // Returns a future object associated with the result of the function called from the GUI thread + template + std::future CallAfter(F&& func) const { - // create task - auto task = std::make_shared>(std::forward(func)); + // Make "shared" promise to workaround std::function limitation + auto spr = std::make_shared>(); - // get future - std::future future = task->get_future(); + // Get future + std::future future = spr->get_future(); - // run asynchronously in GUI thread - m_cb.call_after([=] + // Run asynchronously in GUI thread + m_cb.call_after([spr = std::move(spr), task = std::forward(func)]() { - (*task)(); + try + { + task(); + spr->set_value(); + } + catch (...) + { + spr->set_exception(std::current_exception()); + } }); return future; diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index 17be155bbc..12799ecd8a 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -164,17 +164,19 @@ template struct triplet_t // return 32 bit .size() for container template inline auto size32(const T& container) -> decltype(static_cast(container.size())) { - return container.size() <= UINT32_MAX ? static_cast(container.size()) : throw std::length_error(__FUNCTION__); + const auto size = container.size(); + return size >= 0 && size <= UINT32_MAX ? static_cast(size) : throw std::length_error(__FUNCTION__); } // return 32 bit size for an array template constexpr u32 size32(const T(&)[Size]) { - return Size <= UINT32_MAX ? static_cast(Size) : throw std::length_error(__FUNCTION__); + return Size >= 0 && Size <= UINT32_MAX ? static_cast(Size) : throw std::length_error(__FUNCTION__); } #define WRAP_EXPR(expr) [&]{ return expr; } #define COPY_EXPR(expr) [=]{ return expr; } +#define PURE_EXPR(expr) [] { return expr; } #define EXCEPTION(text, ...) fmt::exception(__FILE__, __LINE__, __FUNCTION__, text, ##__VA_ARGS__) #define VM_CAST(value) vm::impl_cast(value, __FILE__, __LINE__, __FUNCTION__) #define IS_INTEGRAL(t) (std::is_integral::value || std::is_same, u128>::value) @@ -183,6 +185,7 @@ template constexpr u32 size32(const T(&)[Size]) #define CHECK_ASSERTION(expr) if (expr) {} else throw EXCEPTION("Assertion failed: " #expr) #define CHECK_SUCCESS(expr) if (s32 _r = (expr)) throw EXCEPTION(#expr " failed (0x%x)", _r) +// Some forward declarations for the ID manager template struct id_traits; #define _PRGNAME_ "RPCS3"