From f499c8177e661b2fadacb28aebb106e4b16c7ab1 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 14 Jan 2022 16:36:10 -0800 Subject: core: hle: kernel: KThread: Integrate with KWorkerTask and implement DoWorkerTaskImpl. - This is used to terminate a thread asynchronously after it has been exited. - This fixes a crash that can occur in Pokemon Sword/Shield because a thread is incorrectly closed on svcExitThread, then, the thread is destroyed on svcCloseHandle while it is still scheduled. - Instead, we now wait for the thread to no longer be scheduled on all cores before destroying it from KWorkerTaskManager, which is accurate to HOS behavior. --- src/core/hle/kernel/k_thread.cpp | 23 ++++++++++++++++++++++- src/core/hle/kernel/k_thread.h | 7 ++++++- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 3cb995ddb..7a5e6fc08 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -30,6 +30,7 @@ #include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" +#include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/time_manager.h" @@ -332,7 +333,7 @@ void KThread::Finalize() { } // Perform inherited finalization. - KAutoObjectWithSlabHeapAndContainer::Finalize(); + KSynchronizationObject::Finalize(); } bool KThread::IsSignaled() const { @@ -376,11 +377,28 @@ void KThread::StartTermination() { // Register terminated dpc flag. RegisterDpc(DpcFlag::Terminated); +} + +void KThread::FinishTermination() { + // Ensure that the thread is not executing on any core. + if (parent != nullptr) { + for (std::size_t i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { + KThread* core_thread{}; + do { + core_thread = kernel.Scheduler(i).GetCurrentThread(); + } while (core_thread == this); + } + } // Close the thread. this->Close(); } +void KThread::DoWorkerTaskImpl() { + // Finish the termination that was begun by Exit(). + this->FinishTermination(); +} + void KThread::Pin(s32 current_core) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); @@ -1027,6 +1045,9 @@ void KThread::Exit() { // Start termination. StartTermination(); + + // Register the thread as a work task. + KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); } } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 92c3493c5..cc427f6cf 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -19,6 +19,7 @@ #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_worker_task.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/svc_common.h" #include "core/hle/kernel/svc_types.h" @@ -100,7 +101,7 @@ enum class ThreadWaitReasonForDebugging : u32 { [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); -class KThread final : public KAutoObjectWithSlabHeapAndContainer, +class KThread final : public KAutoObjectWithSlabHeapAndContainer, public boost::intrusive::list_base_hook<> { KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); @@ -385,6 +386,8 @@ public: void OnTimer(); + void DoWorkerTaskImpl(); + static void PostDestroy(uintptr_t arg); [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); @@ -679,6 +682,8 @@ private: void StartTermination(); + void FinishTermination(); + [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, KProcess* owner, ThreadType type); -- cgit v1.2.3