From 5ea0d3629a595c18b3b4f825c6b8013009b2e7cc Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 22:32:48 -0700
Subject: [PATCH 01/19] core: cpu_manager: Use jthread.

---
 src/core/cpu_manager.cpp | 25 ++++++++++---------------
 src/core/cpu_manager.h   |  6 +++---
 2 files changed, 13 insertions(+), 18 deletions(-)

diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7e195346b..77efcabf0 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -21,34 +21,25 @@ namespace Core {
 CpuManager::CpuManager(System& system_) : system{system_} {}
 CpuManager::~CpuManager() = default;
 
-void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
-    cpu_manager.RunThread(core);
+void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
+                             std::size_t core) {
+    cpu_manager.RunThread(stop_token, core);
 }
 
 void CpuManager::Initialize() {
     running_mode = true;
     if (is_multicore) {
         for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
-            core_data[core].host_thread =
-                std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
+            core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
         }
     } else {
-        core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
+        core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
     }
 }
 
 void CpuManager::Shutdown() {
     running_mode = false;
     Pause(false);
-    if (is_multicore) {
-        for (auto& data : core_data) {
-            data.host_thread->join();
-            data.host_thread.reset();
-        }
-    } else {
-        core_data[0].host_thread->join();
-        core_data[0].host_thread.reset();
-    }
 }
 
 std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
@@ -317,7 +308,7 @@ void CpuManager::Pause(bool paused) {
     }
 }
 
-void CpuManager::RunThread(std::size_t core) {
+void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
     /// Initialization
     system.RegisterCoreThread(core);
     std::string name;
@@ -361,6 +352,10 @@ void CpuManager::RunThread(std::size_t core) {
             return;
         }
 
+        if (stop_token.stop_requested()) {
+            break;
+        }
+
         auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
         data.is_running = true;
         Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 140263b09..9d92d4af0 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -78,9 +78,9 @@ private:
     void SingleCoreRunSuspendThread();
     void SingleCorePause(bool paused);
 
-    static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
+    static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
 
-    void RunThread(std::size_t core);
+    void RunThread(std::stop_token stop_token, std::size_t core);
 
     struct CoreData {
         std::shared_ptr<Common::Fiber> host_context;
@@ -89,7 +89,7 @@ private:
         std::atomic<bool> is_running;
         std::atomic<bool> is_paused;
         std::atomic<bool> initialized;
-        std::unique_ptr<std::thread> host_thread;
+        std::jthread host_thread;
     };
 
     std::atomic<bool> running_mode{};

From 68eee948758eeddb4f3f091cd89c870e481b278b Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 22:45:18 -0700
Subject: [PATCH 02/19] core: hle: kernel: Reflect non-emulated threads as core
 3.

---
 src/core/core.cpp                            | 6 ------
 src/core/core.h                              | 3 ---
 src/core/hle/kernel/k_address_arbiter.cpp    | 4 ++--
 src/core/hle/kernel/k_condition_variable.cpp | 2 +-
 src/core/hle/kernel/kernel.cpp               | 8 ++++++++
 src/core/hle/kernel/kernel.h                 | 3 +++
 src/core/hle/kernel/svc.cpp                  | 2 +-
 7 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index d3e84c4ef..5d8a61b3a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -494,12 +494,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
     return impl->kernel.CurrentPhysicalCore().ArmInterface();
 }
 
-std::size_t System::CurrentCoreIndex() const {
-    std::size_t core = impl->kernel.GetCurrentHostThreadID();
-    ASSERT(core < Core::Hardware::NUM_CPU_CORES);
-    return core;
-}
-
 Kernel::PhysicalCore& System::CurrentPhysicalCore() {
     return impl->kernel.CurrentPhysicalCore();
 }
diff --git a/src/core/core.h b/src/core/core.h
index ea143043c..cd9af0c07 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -205,9 +205,6 @@ public:
     /// Gets an ARM interface to the CPU core that is currently running
     [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
 
-    /// Gets the index of the currently running CPU core
-    [[nodiscard]] std::size_t CurrentCoreIndex() const;
-
     /// Gets the physical core for the CPU core that is currently running
     [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
 
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1b429bc1e..6771ef621 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
 
 bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
     auto& monitor = system.Monitor();
-    const auto current_core = system.CurrentCoreIndex();
+    const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
 
     // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
     // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
 
 bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
     auto& monitor = system.Monitor();
-    const auto current_core = system.CurrentCoreIndex();
+    const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
 
     // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
     // TODO(bunnei): We should call CanAccessAtomic(..) here.
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index ef14ad1d2..4174f35fd 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
 bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
                       u32 new_orr_mask) {
     auto& monitor = system.Monitor();
-    const auto current_core = system.CurrentCoreIndex();
+    const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
 
     // Load the value from the address.
     const auto expected = monitor.ExclusiveRead32(current_core, address);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 92fbc5532..b0b130719 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -824,6 +824,14 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
     return impl->cores[id];
 }
 
+size_t KernelCore::CurrentPhysicalCoreIndex() const {
+    const u32 core_id = impl->GetCurrentHostThreadID();
+    if (core_id >= Core::Hardware::NUM_CPU_CORES) {
+        return Core::Hardware::NUM_CPU_CORES - 1;
+    }
+    return core_id;
+}
+
 Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
     u32 core_id = impl->GetCurrentHostThreadID();
     ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3a6db0b1c..57535433b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -146,6 +146,9 @@ public:
     /// Gets the an instance of the respective physical CPU core.
     const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
 
+    /// Gets the current physical core index for the running host thread.
+    std::size_t CurrentPhysicalCoreIndex() const;
+
     /// Gets the sole instance of the Scheduler at the current running core.
     Kernel::KScheduler* CurrentScheduler();
 
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2eb532472..a90b291da 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
             const u64 thread_ticks = current_thread->GetCpuTime();
 
             out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
-        } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
+        } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
             out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
         }
 

From 2b9560428b6ab84fc61dd8f82e75f58cdb851c07 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 22:58:46 -0700
Subject: [PATCH 03/19] core: hle: kernel: Ensure idle threads are closed
 before destroying scheduler.

---
 src/core/hle/kernel/k_scheduler.cpp |  6 ++++-
 src/core/hle/kernel/k_scheduler.h   |  2 ++
 src/core/hle/kernel/kernel.cpp      | 38 ++++++++++++-----------------
 3 files changed, 22 insertions(+), 24 deletions(-)

diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6a7d80d03..4bae69f71 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -617,13 +617,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
     state.highest_priority_thread = nullptr;
 }
 
-KScheduler::~KScheduler() {
+void KScheduler::Finalize() {
     if (idle_thread) {
         idle_thread->Close();
         idle_thread = nullptr;
     }
 }
 
+KScheduler::~KScheduler() {
+    ASSERT(!idle_thread);
+}
+
 KThread* KScheduler::GetCurrentThread() const {
     if (auto result = current_thread.load(); result) {
         return result;
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 12cfae919..516e0cdba 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -33,6 +33,8 @@ public:
     explicit KScheduler(Core::System& system_, s32 core_id_);
     ~KScheduler();
 
+    void Finalize();
+
     /// Reschedules to the next available thread (call after current thread is suspended)
     void RescheduleCurrentCore();
 
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b0b130719..6bfb55f71 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -85,8 +85,9 @@ struct KernelCore::Impl {
     }
 
     void InitializeCores() {
-        for (auto& core : cores) {
-            core.Initialize(current_process->Is64BitProcess());
+        for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+            cores[core_id].Initialize(current_process->Is64BitProcess());
+            system.Memory().SetCurrentPageTable(*current_process, core_id);
         }
     }
 
@@ -131,15 +132,6 @@ struct KernelCore::Impl {
         next_user_process_id = KProcess::ProcessIDMin;
         next_thread_id = 1;
 
-        for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
-            if (suspend_threads[core_id]) {
-                suspend_threads[core_id]->Close();
-                suspend_threads[core_id] = nullptr;
-            }
-
-            schedulers[core_id].reset();
-        }
-
         cores.clear();
 
         global_handle_table->Finalize();
@@ -167,6 +159,16 @@ struct KernelCore::Impl {
         CleanupObject(time_shared_mem);
         CleanupObject(system_resource_limit);
 
+        for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+            if (suspend_threads[core_id]) {
+                suspend_threads[core_id]->Close();
+                suspend_threads[core_id] = nullptr;
+            }
+
+            schedulers[core_id]->Finalize();
+            schedulers[core_id].reset();
+        }
+
         // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
         next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
 
@@ -257,14 +259,6 @@ struct KernelCore::Impl {
 
     void MakeCurrentProcess(KProcess* process) {
         current_process = process;
-        if (process == nullptr) {
-            return;
-        }
-
-        const u32 core_id = GetCurrentHostThreadID();
-        if (core_id < Core::Hardware::NUM_CPU_CORES) {
-            system.Memory().SetCurrentPageTable(*process, core_id);
-        }
     }
 
     /// Creates a new host thread ID, should only be called by GetHostThreadId
@@ -1048,13 +1042,11 @@ void KernelCore::ExceptionalExit() {
 }
 
 void KernelCore::EnterSVCProfile() {
-    std::size_t core = impl->GetCurrentHostThreadID();
-    impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
+    impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
 }
 
 void KernelCore::ExitSVCProfile() {
-    std::size_t core = impl->GetCurrentHostThreadID();
-    MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
+    MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
 }
 
 std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {

From 01af2f4162308029e53143f0caa953bc4c59de92 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:04:32 -0700
Subject: [PATCH 04/19] core: hle: kernel: k_thread: Add
 KScopedDisableDispatch.

---
 src/core/hle/kernel/k_thread.cpp | 17 ++++++++++++++++-
 src/core/hle/kernel/k_thread.h   | 31 +++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 9f1d3156b..89653e0cb 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -188,7 +188,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
     // Setup the stack parameters.
     StackParameters& sp = GetStackParameters();
     sp.cur_thread = this;
-    sp.disable_count = 1;
+    sp.disable_count = 0;
     SetInExceptionHandler();
 
     // Set thread ID.
@@ -970,6 +970,9 @@ ResultCode KThread::Run() {
 
         // Set our state and finish.
         SetState(ThreadState::Runnable);
+
+        DisableDispatch();
+
         return ResultSuccess;
     }
 }
@@ -1054,4 +1057,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
     return GetCurrentThread(kernel).GetCurrentCore();
 }
 
+KScopedDisableDispatch::~KScopedDisableDispatch() {
+    if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
+        auto scheduler = kernel.CurrentScheduler();
+
+        if (scheduler) {
+            scheduler->RescheduleCurrentCore();
+        }
+    } else {
+        GetCurrentThread(kernel).EnableDispatch();
+    }
+}
+
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c77f44ad4..a149744c8 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -450,16 +450,35 @@ public:
         sleeping_queue = q;
     }
 
+    [[nodiscard]] bool IsKernelThread() const {
+        return GetActiveCore() == 3;
+    }
+
     [[nodiscard]] s32 GetDisableDispatchCount() const {
+        if (IsKernelThread()) {
+            // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
+            return 1;
+        }
+
         return this->GetStackParameters().disable_count;
     }
 
     void DisableDispatch() {
+        if (IsKernelThread()) {
+            // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
+            return;
+        }
+
         ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
         this->GetStackParameters().disable_count++;
     }
 
     void EnableDispatch() {
+        if (IsKernelThread()) {
+            // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
+            return;
+        }
+
         ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
         this->GetStackParameters().disable_count--;
     }
@@ -752,4 +771,16 @@ public:
     }
 };
 
+class KScopedDisableDispatch {
+public:
+    explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
+        GetCurrentThread(kernel).DisableDispatch();
+    }
+
+    ~KScopedDisableDispatch();
+
+private:
+    KernelCore& kernel;
+};
+
 } // namespace Kernel

From f2b0d289833be94401edb4bb3b5b891fcda713d4 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:05:01 -0700
Subject: [PATCH 05/19] core: hle: kernel: k_handle_table: Use
 KScopedDisableDispatch as necessary.

---
 src/core/hle/kernel/k_handle_table.cpp | 6 ++++++
 src/core/hle/kernel/k_handle_table.h   | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index 6a420d5b0..d720c2dda 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
     // Get the table and clear our record of it.
     u16 saved_table_size = 0;
     {
+        KScopedDisableDispatch dd(kernel);
         KScopedSpinLock lk(m_lock);
 
         std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
     // Find the object and free the entry.
     KAutoObject* obj = nullptr;
     {
+        KScopedDisableDispatch dd(kernel);
         KScopedSpinLock lk(m_lock);
 
         if (this->IsValidHandle(handle)) {
@@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
 }
 
 ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
+    KScopedDisableDispatch dd(kernel);
     KScopedSpinLock lk(m_lock);
 
     // Never exceed our capacity.
@@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
 }
 
 ResultCode KHandleTable::Reserve(Handle* out_handle) {
+    KScopedDisableDispatch dd(kernel);
     KScopedSpinLock lk(m_lock);
 
     // Never exceed our capacity.
@@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
 }
 
 void KHandleTable::Unreserve(Handle handle) {
+    KScopedDisableDispatch dd(kernel);
     KScopedSpinLock lk(m_lock);
 
     // Unpack the handle.
@@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
 }
 
 void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
+    KScopedDisableDispatch dd(kernel);
     KScopedSpinLock lk(m_lock);
 
     // Unpack the handle.
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 2ff6aa160..75dcec7df 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -69,6 +69,7 @@ public:
     template <typename T = KAutoObject>
     KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
         // Lock and look up in table.
+        KScopedDisableDispatch dd(kernel);
         KScopedSpinLock lk(m_lock);
 
         if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -123,6 +124,7 @@ public:
         size_t num_opened;
         {
             // Lock the table.
+            KScopedDisableDispatch dd(kernel);
             KScopedSpinLock lk(m_lock);
             for (num_opened = 0; num_opened < num_handles; num_opened++) {
                 // Get the current handle.

From 7569d6774d2d445d261713b42472fd429ce782a0 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:08:26 -0700
Subject: [PATCH 06/19] core: hle: kernel: k_process: DisableDispatch on main
 thread.

---
 src/core/hle/kernel/k_process.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 8ead1a769..3d7e6707e 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
     thread->GetContext64().cpu_registers[0] = 0;
     thread->GetContext32().cpu_registers[1] = thread_handle;
     thread->GetContext64().cpu_registers[1] = thread_handle;
+    thread->DisableDispatch();
 
     auto& kernel = system.Kernel();
     // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires

From bedcf1971022129ec93ab56b9ccb186fbbb26b7f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:12:47 -0700
Subject: [PATCH 07/19] core: hle: kernel: k_scheduler: Improve Unload.

---
 src/core/hle/kernel/k_scheduler.cpp | 46 ++++++++++++++++++-----------
 1 file changed, 29 insertions(+), 17 deletions(-)

diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 4bae69f71..5ee4b8adc 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -650,6 +650,7 @@ void KScheduler::RescheduleCurrentCore() {
     if (state.needs_scheduling.load()) {
         Schedule();
     } else {
+        GetCurrentThread()->EnableDispatch();
         guard.Unlock();
     }
 }
@@ -659,26 +660,37 @@ void KScheduler::OnThreadStart() {
 }
 
 void KScheduler::Unload(KThread* thread) {
+    ASSERT(thread);
+
+    if (!thread) {
+        return;
+    }
+
     LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
 
-    if (thread) {
-        if (thread->IsCallingSvc()) {
-            thread->ClearIsCallingSvc();
-        }
-        if (!thread->IsTerminationRequested()) {
-            prev_thread = thread;
-
-            Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
-            cpu_core.SaveContext(thread->GetContext32());
-            cpu_core.SaveContext(thread->GetContext64());
-            // Save the TPIDR_EL0 system register in case it was modified.
-            thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
-            cpu_core.ClearExclusiveState();
-        } else {
-            prev_thread = nullptr;
-        }
-        thread->context_guard.Unlock();
+    if (thread->IsCallingSvc()) {
+        thread->ClearIsCallingSvc();
     }
+
+    auto& physical_core = system.Kernel().PhysicalCore(core_id);
+    if (!physical_core.IsInitialized()) {
+        return;
+    }
+
+    Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
+    cpu_core.SaveContext(thread->GetContext32());
+    cpu_core.SaveContext(thread->GetContext64());
+    // Save the TPIDR_EL0 system register in case it was modified.
+    thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+    cpu_core.ClearExclusiveState();
+
+    if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
+        prev_thread = thread;
+    } else {
+        prev_thread = nullptr;
+    }
+
+    thread->context_guard.Unlock();
 }
 
 void KScheduler::Reload(KThread* thread) {

From 77ad64b97d3d5d9d6f5189ad901e2cd98369c973 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:16:12 -0700
Subject: [PATCH 08/19] core: hle: kernel: k_scheduler: Improve ScheduleImpl.

---
 src/core/hle/kernel/k_scheduler.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 5ee4b8adc..e523c4923 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -721,7 +721,7 @@ void KScheduler::SwitchContextStep2() {
 }
 
 void KScheduler::ScheduleImpl() {
-    KThread* previous_thread = current_thread.load();
+    KThread* previous_thread = GetCurrentThread();
     KThread* next_thread = state.highest_priority_thread;
 
     state.needs_scheduling = false;
@@ -733,10 +733,15 @@ void KScheduler::ScheduleImpl() {
 
     // If we're not actually switching thread, there's nothing to do.
     if (next_thread == current_thread.load()) {
+        previous_thread->EnableDispatch();
         guard.Unlock();
         return;
     }
 
+    if (next_thread->GetCurrentCore() != core_id) {
+        next_thread->SetCurrentCore(core_id);
+    }
+
     current_thread.store(next_thread);
 
     KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -747,11 +752,7 @@ void KScheduler::ScheduleImpl() {
     Unload(previous_thread);
 
     std::shared_ptr<Common::Fiber>* old_context;
-    if (previous_thread != nullptr) {
-        old_context = &previous_thread->GetHostContext();
-    } else {
-        old_context = &idle_thread->GetHostContext();
-    }
+    old_context = &previous_thread->GetHostContext();
     guard.Unlock();
 
     Common::Fiber::YieldTo(*old_context, *switch_fiber);

From d1c502720d9adec47047800896594886e7952693 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:16:36 -0700
Subject: [PATCH 09/19] core: hle: kernel: k_scheduler: Remove unnecessary
 MakeCurrentProcess.

---
 src/core/hle/kernel/k_scheduler.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index e523c4923..f5236dfea 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -699,11 +699,6 @@ void KScheduler::Reload(KThread* thread) {
     if (thread) {
         ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
 
-        auto* const thread_owner_process = thread->GetOwnerProcess();
-        if (thread_owner_process != nullptr) {
-            system.Kernel().MakeCurrentProcess(thread_owner_process);
-        }
-
         Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
         cpu_core.LoadContext(thread->GetContext32());
         cpu_core.LoadContext(thread->GetContext64());

From 2dfb07388ac4755a2b43944cc9a3edfa531a2fee Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:20:07 -0700
Subject: [PATCH 10/19] core: hle: kernel: Use CurrentPhysicalCoreIndex as
 appropriate.

---
 src/core/hle/kernel/kernel.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 6bfb55f71..2a984b913 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -827,15 +827,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const {
 }
 
 Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
-    u32 core_id = impl->GetCurrentHostThreadID();
-    ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
-    return impl->cores[core_id];
+    return impl->cores[CurrentPhysicalCoreIndex()];
 }
 
 const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
-    u32 core_id = impl->GetCurrentHostThreadID();
-    ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
-    return impl->cores[core_id];
+    return impl->cores[CurrentPhysicalCoreIndex()];
 }
 
 Kernel::KScheduler* KernelCore::CurrentScheduler() {

From cbe4e32d38c6cc844f5fbf4fc59831a7abea08a9 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:22:52 -0700
Subject: [PATCH 11/19] core: cpu_manager: Use KScopedDisableDispatch.

---
 src/core/cpu_manager.cpp | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 77efcabf0..e2c4f0e07 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -118,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
             physical_core = &kernel.CurrentPhysicalCore();
         }
         system.ExitDynarmicProfile();
-        physical_core->ArmInterface().ClearExclusiveState();
-        kernel.CurrentScheduler()->RescheduleCurrentCore();
+        {
+            Kernel::KScopedDisableDispatch dd(kernel);
+            physical_core->ArmInterface().ClearExclusiveState();
+        }
     }
 }
 
 void CpuManager::MultiCoreRunIdleThread() {
     auto& kernel = system.Kernel();
     while (true) {
-        auto& physical_core = kernel.CurrentPhysicalCore();
-        physical_core.Idle();
-        kernel.CurrentScheduler()->RescheduleCurrentCore();
+        Kernel::KScopedDisableDispatch dd(kernel);
+        kernel.CurrentPhysicalCore().Idle();
     }
 }
 
@@ -136,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
     auto& kernel = system.Kernel();
     kernel.CurrentScheduler()->OnThreadStart();
     while (true) {
-        auto core = kernel.GetCurrentHostThreadID();
+        auto core = kernel.CurrentPhysicalCoreIndex();
         auto& scheduler = *kernel.CurrentScheduler();
         Kernel::KThread* current_thread = scheduler.GetCurrentThread();
         Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
         ASSERT(scheduler.ContextSwitchPending());
-        ASSERT(core == kernel.GetCurrentHostThreadID());
+        ASSERT(core == kernel.CurrentPhysicalCoreIndex());
         scheduler.RescheduleCurrentCore();
     }
 }

From 1798c3b6b0cd4a088be821c604c7ff1617afde7b Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:27:33 -0700
Subject: [PATCH 12/19] core: hle: kernel: k_scheduler: Improve
 DisableScheduling and EnableScheduling.

---
 src/core/hle/kernel/k_scheduler.cpp | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index f5236dfea..6ddbae52c 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
 }
 
 void KScheduler::DisableScheduling(KernelCore& kernel) {
-    if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
-        ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
-        scheduler->GetCurrentThread()->DisableDispatch();
-    }
+    ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
+    GetCurrentThreadPointer(kernel)->DisableDispatch();
 }
 
 void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
-    if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
-        ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
-        if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
-            scheduler->GetCurrentThread()->EnableDispatch();
-        }
+    ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1);
+
+    if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) {
+        GetCurrentThreadPointer(kernel)->EnableDispatch();
+    } else {
+        RescheduleCores(kernel, cores_needing_scheduling);
     }
-    RescheduleCores(kernel, cores_needing_scheduling);
 }
 
 u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -646,6 +644,7 @@ void KScheduler::RescheduleCurrentCore() {
     if (phys_core.IsInterrupted()) {
         phys_core.ClearInterrupt();
     }
+
     guard.Lock();
     if (state.needs_scheduling.load()) {
         Schedule();
@@ -662,10 +661,6 @@ void KScheduler::OnThreadStart() {
 void KScheduler::Unload(KThread* thread) {
     ASSERT(thread);
 
-    if (!thread) {
-        return;
-    }
-
     LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
 
     if (thread->IsCallingSvc()) {

From 5051d3c4159adabe4c3e8b9233a524894eed6808 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 6 Aug 2021 23:43:26 -0700
Subject: [PATCH 13/19] core: hle: kernel: DisableDispatch on suspend threads.

---
 src/core/hle/kernel/kernel.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 2a984b913..8673384ee 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1024,6 +1024,9 @@ void KernelCore::Suspend(bool in_suspention) {
             impl->suspend_threads[core_id]->SetState(state);
             impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
                 ThreadWaitReasonForDebugging::Suspended);
+            if (!should_suspend) {
+                impl->suspend_threads[core_id]->DisableDispatch();
+            }
         }
     }
 }

From 36cf96857eab706c07470e228542900c41dbabd1 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 7 Aug 2021 01:05:46 -0700
Subject: [PATCH 14/19] core: hle: service: nvflinger/vi: Improve management of
 KEvent.

---
 src/core/hle/service/nvflinger/nvflinger.cpp   | 13 +++++++------
 src/core/hle/service/nvflinger/nvflinger.h     |  3 +++
 src/core/hle/service/vi/display/vi_display.cpp | 17 ++++++++++-------
 src/core/hle/service/vi/display/vi_display.h   | 13 ++++++++++---
 4 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 941748970..32d4e360a 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() {
     }
 }
 
-NVFlinger::NVFlinger(Core::System& system_) : system(system_) {
-    displays.emplace_back(0, "Default", system);
-    displays.emplace_back(1, "External", system);
-    displays.emplace_back(2, "Edid", system);
-    displays.emplace_back(3, "Internal", system);
-    displays.emplace_back(4, "Null", system);
+NVFlinger::NVFlinger(Core::System& system_)
+    : system(system_), service_context(system_, "nvflinger") {
+    displays.emplace_back(0, "Default", service_context, system);
+    displays.emplace_back(1, "External", service_context, system);
+    displays.emplace_back(2, "Edid", service_context, system);
+    displays.emplace_back(3, "Internal", service_context, system);
+    displays.emplace_back(4, "Null", service_context, system);
     guard = std::make_shared<std::mutex>();
 
     // Schedule the screen composition events
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index d80fd07ef..6d84cafb4 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "common/common_types.h"
+#include "core/hle/service/kernel_helpers.h"
 
 namespace Common {
 class Event;
@@ -135,6 +136,8 @@ private:
     std::unique_ptr<std::thread> vsync_thread;
     std::unique_ptr<Common::Event> wait_event;
     std::atomic<bool> is_running{};
+
+    KernelHelpers::ServiceContext service_context;
 };
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 0dd342dbf..b7705c02a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -12,18 +12,21 @@
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/service/kernel_helpers.h"
 #include "core/hle/service/vi/display/vi_display.h"
 #include "core/hle/service/vi/layer/vi_layer.h"
 
 namespace Service::VI {
 
-Display::Display(u64 id, std::string name_, Core::System& system)
-    : display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} {
-    Kernel::KAutoObject::Create(std::addressof(vsync_event));
-    vsync_event.Initialize(fmt::format("Display VSync Event {}", id));
+Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
+                 Core::System& system_)
+    : display_id{id}, name{std::move(name_)}, service_context{service_context_} {
+    vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
 }
 
-Display::~Display() = default;
+Display::~Display() {
+    service_context.CloseEvent(vsync_event);
+}
 
 Layer& Display::GetLayer(std::size_t index) {
     return *layers.at(index);
@@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const {
 }
 
 Kernel::KReadableEvent& Display::GetVSyncEvent() {
-    return vsync_event.GetReadableEvent();
+    return vsync_event->GetReadableEvent();
 }
 
 void Display::SignalVSyncEvent() {
-    vsync_event.GetWritableEvent().Signal();
+    vsync_event->GetWritableEvent().Signal();
 }
 
 void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 166f2a4cc..0979fc421 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -18,6 +18,9 @@ class KEvent;
 namespace Service::NVFlinger {
 class BufferQueue;
 }
+namespace Service::KernelHelpers {
+class ServiceContext;
+} // namespace Service::KernelHelpers
 
 namespace Service::VI {
 
@@ -31,10 +34,13 @@ class Display {
 public:
     /// Constructs a display with a given unique ID and name.
     ///
-    /// @param id   The unique ID for this display.
+    /// @param id The unique ID for this display.
+    /// @param service_context_ The ServiceContext for the owning service.
     /// @param name_ The name for this display.
+    /// @param system_ The global system instance.
     ///
-    Display(u64 id, std::string name_, Core::System& system);
+    Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
+            Core::System& system_);
     ~Display();
 
     /// Gets the unique ID assigned to this display.
@@ -98,9 +104,10 @@ public:
 private:
     u64 display_id;
     std::string name;
+    KernelHelpers::ServiceContext& service_context;
 
     std::vector<std::shared_ptr<Layer>> layers;
-    Kernel::KEvent vsync_event;
+    Kernel::KEvent* vsync_event{};
 };
 
 } // namespace Service::VI

From 48a3496b9390e0e1cd0a649d941360dda7b9839b Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 7 Aug 2021 01:16:29 -0700
Subject: [PATCH 15/19] core: hle: kernel: k_auto_object: Add GetName method.

- Useful purely for debugging.
---
 src/core/hle/kernel/k_auto_object.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index e4fcdbc67..165b76747 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -170,6 +170,10 @@ public:
         }
     }
 
+    const std::string& GetName() const {
+        return name;
+    }
+
 private:
     void RegisterWithKernel();
     void UnregisterWithKernel();

From 99bc49e76e85fbf44142d8f925e02bc27c7fa6da Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 7 Aug 2021 01:17:13 -0700
Subject: [PATCH 16/19] core: hle: service: buffer_queue: Improve management of
 KEvent.

---
 .../hle/service/nvflinger/buffer_queue.cpp    | 25 +++++++++++--------
 src/core/hle/service/nvflinger/buffer_queue.h | 11 ++++++--
 src/core/hle/service/nvflinger/nvflinger.cpp  |  2 +-
 3 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 59ddf6298..b4c3a6099 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,17 +9,20 @@
 #include "core/core.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
+#include "core/hle/service/kernel_helpers.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
 
 namespace Service::NVFlinger {
 
-BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_)
-    : id(id_), layer_id(layer_id_), buffer_wait_event{kernel} {
-    Kernel::KAutoObject::Create(std::addressof(buffer_wait_event));
-    buffer_wait_event.Initialize("BufferQueue:WaitEvent");
+BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
+                         KernelHelpers::ServiceContext& service_context_)
+    : id(id_), layer_id(layer_id_), service_context{service_context_} {
+    buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
 }
 
-BufferQueue::~BufferQueue() = default;
+BufferQueue::~BufferQueue() {
+    service_context.CloseEvent(buffer_wait_event);
+}
 
 void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
     ASSERT(slot < buffer_slots);
@@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
         .multi_fence = {},
     };
 
-    buffer_wait_event.GetWritableEvent().Signal();
+    buffer_wait_event->GetWritableEvent().Signal();
 }
 
 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
@@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
     }
     free_buffers_condition.notify_one();
 
-    buffer_wait_event.GetWritableEvent().Signal();
+    buffer_wait_event->GetWritableEvent().Signal();
 }
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
@@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
     }
     free_buffers_condition.notify_one();
 
-    buffer_wait_event.GetWritableEvent().Signal();
+    buffer_wait_event->GetWritableEvent().Signal();
 }
 
 void BufferQueue::Connect() {
@@ -169,7 +172,7 @@ void BufferQueue::Disconnect() {
         std::unique_lock lock{queue_sequence_mutex};
         queue_sequence.clear();
     }
-    buffer_wait_event.GetWritableEvent().Signal();
+    buffer_wait_event->GetWritableEvent().Signal();
     is_connect = false;
     free_buffers_condition.notify_one();
 }
@@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) {
 }
 
 Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
-    return buffer_wait_event.GetWritableEvent();
+    return buffer_wait_event->GetWritableEvent();
 }
 
 Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
-    return buffer_wait_event.GetReadableEvent();
+    return buffer_wait_event->GetReadableEvent();
 }
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 61e337ac5..759247eb0 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -24,6 +24,10 @@ class KReadableEvent;
 class KWritableEvent;
 } // namespace Kernel
 
+namespace Service::KernelHelpers {
+class ServiceContext;
+} // namespace Service::KernelHelpers
+
 namespace Service::NVFlinger {
 
 constexpr u32 buffer_slots = 0x40;
@@ -54,7 +58,8 @@ public:
         NativeWindowFormat = 2,
     };
 
-    explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_);
+    explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
+                         KernelHelpers::ServiceContext& service_context_);
     ~BufferQueue();
 
     enum class BufferTransformFlags : u32 {
@@ -130,12 +135,14 @@ private:
     std::list<u32> free_buffers;
     std::array<Buffer, buffer_slots> buffers;
     std::list<u32> queue_sequence;
-    Kernel::KEvent buffer_wait_event;
+    Kernel::KEvent* buffer_wait_event{};
 
     std::mutex free_buffers_mutex;
     std::condition_variable free_buffers_condition;
 
     std::mutex queue_sequence_mutex;
+
+    KernelHelpers::ServiceContext& service_context;
 };
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 32d4e360a..00bff8caf 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -147,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
 void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
     const u32 buffer_queue_id = next_buffer_queue_id++;
     buffer_queues.emplace_back(
-        std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
+        std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
     display.CreateLayer(layer_id, *buffer_queues.back());
 }
 

From 9e3d1d865c4baf2cfc3bf937136eb7625091603f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 7 Aug 2021 12:33:07 -0700
Subject: [PATCH 17/19] core: cpu_manager: Use invalid core_id on init and
 simplify shutdown.

---
 src/core/cpu_manager.cpp | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index e2c4f0e07..35c1a6cbd 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -33,7 +33,7 @@ void CpuManager::Initialize() {
             core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
         }
     } else {
-        core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
+        core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), -1);
     }
 }
 
@@ -348,13 +348,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
             sc_sync_first_use = false;
         }
 
-        // Abort if emulation was killed before the session really starts
-        if (!system.IsPoweredOn()) {
-            return;
-        }
-
+        // Emulation was stopped
         if (stop_token.stop_requested()) {
-            break;
+            return;
         }
 
         auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();

From 5060a9721091b3cc994a73569bfd5ab573b9f47d Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 7 Aug 2021 12:33:31 -0700
Subject: [PATCH 18/19] core: hle: kernel: k_thread: Mark
 KScopedDisableDispatch as nodiscard.

---
 src/core/hle/kernel/k_thread.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index a149744c8..8bbf66c52 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -773,7 +773,7 @@ public:
 
 class KScopedDisableDispatch {
 public:
-    explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
+    [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
         GetCurrentThread(kernel).DisableDispatch();
     }
 

From aef0ca6f0d422623f46f47c15e9a4c9b5fd04dd0 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 14 Aug 2021 02:14:19 -0700
Subject: [PATCH 19/19] core: hle: kernel: Disable dispatch count tracking on
 single core.

- This would have limited value, and would be a mess to handle properly.
---
 src/core/cpu_manager.cpp         |  2 +-
 src/core/hle/kernel/k_thread.cpp |  4 +++-
 src/core/hle/kernel/k_thread.h   | 11 ++++++++---
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 35c1a6cbd..de2e5563e 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -33,7 +33,7 @@ void CpuManager::Initialize() {
             core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
         }
     } else {
-        core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), -1);
+        core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
     }
 }
 
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 89653e0cb..0f6808ade 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -14,6 +14,7 @@
 #include "common/fiber.h"
 #include "common/logging/log.h"
 #include "common/scope_exit.h"
+#include "common/settings.h"
 #include "common/thread_queue_list.h"
 #include "core/core.h"
 #include "core/cpu_manager.h"
@@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
     // Initialize the thread.
     R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
 
-    // Initialize host context.
+    // Initialize emulation parameters.
     thread->host_context =
         std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
+    thread->is_single_core = !Settings::values.use_multi_core.GetValue();
 
     return ResultSuccess;
 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 8bbf66c52..e4c4c877d 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -454,8 +454,12 @@ public:
         return GetActiveCore() == 3;
     }
 
+    [[nodiscard]] bool IsDispatchTrackingDisabled() const {
+        return is_single_core || IsKernelThread();
+    }
+
     [[nodiscard]] s32 GetDisableDispatchCount() const {
-        if (IsKernelThread()) {
+        if (IsDispatchTrackingDisabled()) {
             // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
             return 1;
         }
@@ -464,7 +468,7 @@ public:
     }
 
     void DisableDispatch() {
-        if (IsKernelThread()) {
+        if (IsDispatchTrackingDisabled()) {
             // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
             return;
         }
@@ -474,7 +478,7 @@ public:
     }
 
     void EnableDispatch() {
-        if (IsKernelThread()) {
+        if (IsDispatchTrackingDisabled()) {
             // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
             return;
         }
@@ -727,6 +731,7 @@ private:
 
     // For emulation
     std::shared_ptr<Common::Fiber> host_context{};
+    bool is_single_core{};
 
     // For debugging
     std::vector<KSynchronizationObject*> wait_objects_for_debugging;