diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 4350915d70..37c4a56f4c 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -33,7 +33,8 @@ enum class thread_state : u32 aborting = 1, // The thread has been joined in the destructor or explicitly aborted errored = 2, // Set after the emergency_exit call finished = 3, // Final state, always set at the end of thread execution - mask = 3 + mask = 3, + destroying_context = 7, // Special value assigned to destroy data explicitly before the destructor }; template @@ -702,14 +703,17 @@ public: thread::m_sync.notify_all(); } - if (s == thread_state::finished) + if (s == thread_state::finished || s == thread_state::destroying_context) { // This participates in emulation stopping, use destruction-alike semantics thread::join(true); + } + if (s == thread_state::destroying_context) + { if constexpr (std::is_assignable_v) { - static_cast(*this) = thread_state::finished; + static_cast(*this) = thread_state::destroying_context; } } diff --git a/rpcs3/Emu/Cell/lv2/sys_config.cpp b/rpcs3/Emu/Cell/lv2/sys_config.cpp index b254af8206..6740759d3b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_config.cpp @@ -122,7 +122,7 @@ void lv2_config::remove_service_event(u32 id) lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) noexcept { - if (s == thread_state::finished) + if (s == thread_state::destroying_context && !m_destroyed.exchange(true)) { if (auto global = g_fxo->try_get()) { diff --git a/rpcs3/Emu/Cell/lv2/sys_config.h b/rpcs3/Emu/Cell/lv2/sys_config.h index 0d7fede8b1..ea9b9da76d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.h +++ b/rpcs3/Emu/Cell/lv2/sys_config.h @@ -362,6 +362,10 @@ class lv2_config_service_event return g_fxo->get().next_id++; } + atomic_t m_destroyed = false; + + friend class lv2_config; + public: const u32 id; @@ -393,8 +397,7 @@ public: // Destructor lv2_config_service_event& operator=(thread_state s) noexcept; - - ~lv2_config_service_event() noexcept = default; + ~lv2_config_service_event() noexcept; // Notify queue that this event exists bool notify() const; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp index d95c6935fc..7bdd18b3fd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp @@ -178,7 +178,7 @@ void lv2_socket::queue_wake(ppu_thread* ppu) lv2_socket& lv2_socket::operator=(thread_state s) noexcept { - if (s == thread_state::finished) + if (s == thread_state::destroying_context) { close(); } diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 26162045b7..7846d6f70f 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -805,8 +805,8 @@ public: { if (ptr) { - constexpr thread_state finished{3}; - *static_cast(ptr.get()) = finished; + constexpr thread_state destroying_context{7}; + *static_cast(ptr.get()) = destroying_context; } } @@ -837,8 +837,8 @@ public: { if (ptr) { - constexpr thread_state finished{3}; - *static_cast(ptr.get()) = finished; + constexpr thread_state destroying_context{7}; + *static_cast(ptr.get()) = destroying_context; } } diff --git a/rpcs3/util/fixed_typemap.hpp b/rpcs3/util/fixed_typemap.hpp index c742f8b03b..85d04c57a1 100644 --- a/rpcs3/util/fixed_typemap.hpp +++ b/rpcs3/util/fixed_typemap.hpp @@ -347,6 +347,19 @@ namespace stx } } + // Order semi-destructors before the actual destructors + // This allows to safely access data that may be deallocated or destroyed from other members of FXO regardless of their intialization time + for (u32 i = 0; i < _max; i++) + { + const auto info = (*std::prev(m_info, i + 1)); + + if (auto op = info->thread_op) + { + constexpr thread_state destroying_context{7}; + op(*std::prev(m_order, i + 1), destroying_context); + } + } + // Destroy objects in reverse order for (; _max; _max--) {