From d19e6086fa85aa91f58473b324f7bb876b6a4192 Mon Sep 17 00:00:00 2001 From: Masamune3210 <1053504+Masamune3210@users.noreply.github.com> Date: Thu, 14 May 2026 19:21:59 -0500 Subject: [PATCH] kernel: suppress async IPC wakeups during shutdown Async IPC continuations can finish while System::Shutdown is tearing down service, kernel, and timing state. The dump showed an HTTP ReceiveData RunAsync task calling Thread::WakeAfterDelay after shutdown had begun, which reached Core::Timing::ScheduleEvent with invalid timing state and crashed. Mark KernelSystem as shutting down at the start of System::Shutdown and in the kernel destructor. Route RunAsync and RunOnThreadWorker completions through HLERequestContext::WakeAfterAsync, which skips scheduling guest-thread wakes once shutdown starts and balances the pending async counter instead. Validation: git diff --check --- src/core/core.cpp | 3 +++ src/core/hle/kernel/hle_ipc.cpp | 9 +++++++++ src/core/hle/kernel/hle_ipc.h | 6 ++++-- src/core/hle/kernel/kernel.cpp | 1 + src/core/hle/kernel/kernel.h | 9 +++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index f9dfe1534..4387d1aee 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -692,6 +692,9 @@ void System::Shutdown(bool is_deserializing) { // Shutdown emulation session is_powered_on = false; + if (kernel) { + kernel->BeginShutdown(); + } gpu.reset(); if (!is_deserializing) { diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index fe82b39b7..0bbdb3916 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -135,6 +135,15 @@ HLERequestContext::HLERequestContext(KernelSystem& kernel, std::shared_ptrWakeAfterDelay(sleep_for, true); +} + std::shared_ptr HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { ASSERT(id_from_cmdbuf < request_handles.size()); return request_handles[id_from_cmdbuf]; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index bdd7877fc..80921e7ab 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -259,6 +259,8 @@ public: std::shared_ptr callback); private: + void WakeAfterAsync(s64 sleep_for); + template class AsyncWakeUpCallback : public WakeupCallback { public: @@ -311,7 +313,7 @@ public: kernel, result_function, std::move(std::async(std::launch::async, [this, async_section] { s64 sleep_for = async_section(*this); - this->thread->WakeAfterDelay(sleep_for, true); + WakeAfterAsync(sleep_for); })))); } else { @@ -351,7 +353,7 @@ public: // We use packaged_task so we can retrieve a std::future to pass to AsyncWakeUpCallback auto task = std::make_shared>([this, async_section] { s64 sleep_for = async_section(*this); - this->thread->WakeAfterDelay(sleep_for, true); + WakeAfterAsync(sleep_for); }); auto future = task->get_future(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 5c9e5b6ab..1fed063f8 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -48,6 +48,7 @@ KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, /// Shutdown the kernel KernelSystem::~KernelSystem() { + BeginShutdown(); ResetThreadIDs(); }; diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 4637d1de0..338a13509 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -410,6 +410,14 @@ public: return pending_async_operations != 0; } + void BeginShutdown() { + shutting_down.store(true, std::memory_order_release); + } + + bool IsShuttingDown() const { + return shutting_down.load(std::memory_order_acquire); + } + void UpdateCPUAndMemoryState(u64 title_id, MemoryMode memory_mode, New3dsHwCapabilities n3ds_hw_cap); @@ -431,6 +439,7 @@ private: std::atomic next_object_id{0}; std::atomic pending_async_operations{}; + std::atomic shutting_down{false}; // Note: keep the member order below in order to perform correct destruction. // Thread manager is destructed before process list in order to Stop threads and clear thread