From cc112f971ebd6bcd0e891d21a8ddb8ee4dbe2dbc Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 14 Jan 2022 16:02:57 -0800
Subject: hle: kernel: Fix service_threads access to be thread safe V2.

- PR #7699 attempted to fix CreateServiceThread and ReleaseServiceThread to be thread safe, but inadvertently introduced a possible dead-lock.
- With this PR, we use a worker thread to manage the service thread list, allowing it only to be accessed by a single thread, and guaranteeing threads will not destroy themselves.
- Fixes a rare crash in Pokemon Sword/Shield, I've now run this game for ~12 hours non-stop and am quite confident this is a good solution for this issue.
---
 src/core/hle/kernel/kernel.cpp | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

(limited to 'src/core/hle/kernel/kernel.cpp')

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e1e17db13d..0b618fb46f 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -51,7 +51,8 @@ namespace Kernel {
 
 struct KernelCore::Impl {
     explicit Impl(Core::System& system_, KernelCore& kernel_)
-        : time_manager{system_}, object_list_container{kernel_}, system{system_} {}
+        : time_manager{system_}, object_list_container{kernel_},
+          service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
 
     void SetMulticore(bool is_multi) {
         is_multicore = is_multi;
@@ -707,24 +708,22 @@ struct KernelCore::Impl {
     std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
                                                              const std::string& name) {
         auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
-        {
-            std::lock_guard lk(service_threads_lock);
-            service_threads.emplace(service_thread);
-        }
+
+        service_threads_manager.QueueWork(
+            [this, service_thread]() { service_threads.emplace(service_thread); });
+
         return service_thread;
     }
 
     void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
-        auto strong_ptr = service_thread.lock();
-        {
-            std::lock_guard lk(service_threads_lock);
-            service_threads.erase(strong_ptr);
+        if (auto strong_ptr = service_thread.lock()) {
+            service_threads_manager.QueueWork(
+                [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
         }
     }
 
     void ClearServiceThreads() {
-        std::lock_guard lk(service_threads_lock);
-        service_threads.clear();
+        service_threads_manager.QueueWork([this]() { service_threads.clear(); });
     }
 
     std::mutex server_ports_lock;
@@ -732,7 +731,6 @@ struct KernelCore::Impl {
     std::mutex registered_objects_lock;
     std::mutex registered_in_use_objects_lock;
     std::mutex dummy_thread_lock;
-    std::mutex service_threads_lock;
 
     std::atomic<u32> next_object_id{0};
     std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
@@ -783,6 +781,7 @@ struct KernelCore::Impl {
 
     // Threads used for services
     std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
+    Common::ThreadWorker service_threads_manager;
 
     std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
     std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
-- 
cgit v1.2.3-70-g09d2