diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index a035ea31f3..1414bd540b 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1755,6 +1755,8 @@ const bool s_terminate_handler_set = []() -> bool thread_local DECLARE(thread_ctrl::g_tls_this_thread) = nullptr; +thread_local DECLARE(thread_ctrl::g_tls_error_callback) = nullptr; + DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined }; void thread_base::start(native_entry entry) @@ -1767,11 +1769,13 @@ void thread_base::start(native_entry entry) #endif } -void thread_base::initialize(bool(*wait_cb)(const void*)) +void thread_base::initialize(void (*error_cb)(), bool(*wait_cb)(const void*)) { - // Initialize TLS variable + // Initialize TLS variables thread_ctrl::g_tls_this_thread = this; + thread_ctrl::g_tls_error_callback = error_cb; + // Initialize atomic wait callback atomic_storage_futex::set_wait_callback(wait_cb); @@ -1828,7 +1832,7 @@ void thread_base::notify_abort() noexcept atomic_storage_futex::raw_notify(+m_state_notifier); } -bool thread_base::finalize(int) noexcept +bool thread_base::finalize(thread_state result_state) noexcept { // Report pending errors error_code::error_report(0, 0, 0, 0); @@ -1875,7 +1879,7 @@ bool thread_base::finalize(int) noexcept fsoft, fhard, ctxvol, ctxinv); // Return true if need to delete thread object - const bool ok = m_state.exchange(thread_state::finished) <= thread_state::aborting; + const bool ok = m_state.exchange(result_state) <= thread_state::aborting; // Signal waiting threads m_state.notify_all(); @@ -1984,13 +1988,18 @@ thread_base::~thread_base() } } -void thread_base::join() const +bool thread_base::join() const { for (auto state = m_state.load(); state != thread_state::finished;) { m_state.wait(state); state = m_state; + + if (state == thread_state::errored) + return false; } + + return true; } void thread_base::notify() @@ -2061,7 +2070,9 @@ void thread_ctrl::emergency_exit(std::string_view reason) if (const auto _this = g_tls_this_thread) { - if (_this->finalize(0)) + g_tls_error_callback(); + + if (_this->finalize(thread_state::errored)) { delete _this; } diff --git a/Utilities/Thread.h b/Utilities/Thread.h index e58e8fad90..6df879e756 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -36,6 +36,7 @@ enum class thread_state : u32 { created, // Initial state aborting, // The thread has been joined in the destructor or explicitly aborted + errored, // Set after the emergency_exit call finished // Final state, always set at the end of thread execution }; @@ -45,6 +46,8 @@ class named_thread; template struct result_storage { + static_assert(std::is_default_constructible_v && noexcept(T())); + alignas(T) std::byte data[sizeof(T)]; static constexpr bool empty = false; @@ -116,13 +119,13 @@ class thread_base void start(native_entry); // Called at the thread start - void initialize(bool(*wait_cb)(const void*)); + void initialize(void (*error_cb)(), bool(*wait_cb)(const void*)); // May be called in destructor void notify_abort() noexcept; // Called at the thread end, returns true if needs destruction - bool finalize(int) noexcept; + bool finalize(thread_state result) noexcept; // Cleanup after possibly deleting the thread instance static void finalize() noexcept; @@ -142,7 +145,7 @@ public: u64 get_cycles(); // Wait for the thread (it does NOT change thread state, and can be called from multiple threads) - void join() const; + bool join() const; // Notify the thread void notify(); @@ -154,6 +157,9 @@ class thread_ctrl final // Current thread static thread_local thread_base* g_tls_this_thread; + // Error handling details + static thread_local void(*g_tls_error_callback)(); + // Target cpu core layout static atomic_t g_native_core_layout; @@ -272,7 +278,18 @@ class named_thread final : public Context, result_storage_t, thread_bas bool entry_point() { - thread::initialize([](const void* data) + auto tls_error_cb = []() + { + const auto _this = thread_ctrl::get_current(); + + if constexpr (!result::empty) + { + // Construct using default constructor in the case of failure + new (static_cast(static_cast(_this))->get()) typename result::type(); + } + }; + + thread::initialize(tls_error_cb, [](const void* data) { const auto _this = thread_ctrl::get_current(); @@ -308,7 +325,7 @@ class named_thread final : public Context, result_storage_t, thread_bas new (result::get()) typename result::type(Context::operator()()); } - return thread::finalize(0); + return thread::finalize(thread_state::finished); } friend class thread_ctrl; @@ -437,12 +454,19 @@ public: named_thread_group& operator=(const named_thread_group&) = delete; // Wait for completion - void join() const + bool join() const { + bool result = true; + for (u32 i = 0; i < m_count; i++) { std::as_const(*std::launder(m_threads + i))(); + + if (std::as_const(*std::launder(m_threads + i)) != thread_state::finished) + result = false; } + + return result; } // Join and access specific thread diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp index 19c3c853f4..9301cc6d65 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp @@ -257,7 +257,7 @@ namespace rsx } else { - while (!exit && thread_ctrl::state() == thread_state::created) + while (!exit && thread_ctrl::state() <= thread_state::aborting) { refresh(); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index c0f12b8cb0..d56f699deb 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -621,7 +621,7 @@ bool Emulator::InstallPkg(const std::string& path) { // Wait for the completion - while (std::this_thread::sleep_for(5ms), worker != thread_state::finished) + while (std::this_thread::sleep_for(5ms), worker <= thread_state::aborting) { // TODO: update unified progress dialog double pval = progress; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 3c5a3cb1dd..b0e8d287cb 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -513,7 +513,7 @@ void main_window::HandlePackageInstallation(QStringList file_paths) }); // Wait for the completion - while (std::this_thread::sleep_for(5ms), worker != thread_state::finished) + while (std::this_thread::sleep_for(5ms), worker <= thread_state::aborting) { if (pdlg.wasCanceled()) {