From dc39d06950de246094be9643313970125e0c49ee Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Fri, 17 Jul 2015 02:24:13 -0300
Subject: [PATCH] Ensure all kernel objects are released during shutdown

This commit fixes several kernel object leaks. The most severe of them
was threads not being removed from the private handle table used for
CoreTiming events. This resulted in Threads never being released, which
in turn held references to Process, causing CodeSets to never be freed
when loading other applications.
---
 src/core/hle/kernel/thread.cpp   | 21 ++++++++++++++-------
 src/core/hle/service/apt/apt.cpp |  3 +++
 src/core/hle/service/dsp_dsp.cpp |  5 +++++
 src/core/hle/service/dsp_dsp.h   |  1 +
 src/core/hle/service/gsp_gpu.cpp |  7 ++++++-
 src/core/hle/service/gsp_gpu.h   |  1 +
 src/core/hle/service/nwm_uds.cpp |  4 ++++
 src/core/hle/service/nwm_uds.h   |  1 +
 src/core/hle/service/srv.cpp     |  4 ++++
 src/core/hle/service/srv.h       |  1 +
 src/core/hle/service/y2r_u.cpp   |  4 ++++
 src/core/hle/service/y2r_u.h     |  1 +
 12 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 4729a7fe0..64166ab99 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -37,6 +37,10 @@ void Thread::Acquire() {
     ASSERT_MSG(!ShouldWait(), "object unavailable!");
 }
 
+// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
+//               us to simply use a pool index or similar.
+static Kernel::HandleTable wakeup_callback_handle_table;
+
 // Lists all thread ids that aren't deleted/etc.
 static std::vector<SharedPtr<Thread>> thread_list;
 
@@ -93,6 +97,8 @@ void Thread::Stop() {
 
     // Cancel any outstanding wakeup events for this thread
     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
+    wakeup_callback_handle_table.Close(callback_handle);
+    callback_handle = 0;
 
     // Clean up thread from ready queue
     // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
@@ -108,6 +114,7 @@ void Thread::Stop() {
     for (auto& wait_object : wait_objects) {
         wait_object->RemoveWaitingThread(this);
     }
+    wait_objects.clear();
 
     Kernel::g_current_process->used_tls_slots[tls_index] = false;
 
@@ -268,10 +275,6 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
     thread->status = THREADSTATUS_WAIT_ARB;
 }
 
-// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
-//               us to simply use a pool index or similar.
-static Kernel::HandleTable wakeup_callback_handle_table;
-
 /**
  * Callback that will wake up the thread it was scheduled for
  * @param thread_handle The handle of the thread that's been awoken
@@ -503,12 +506,16 @@ void ThreadingInit() {
 
     current_thread = nullptr;
     next_thread_id = 1;
+}
 
+void ThreadingShutdown() {
+    current_thread = nullptr;
+
+    for (auto& t : thread_list) {
+        t->Stop();
+    }
     thread_list.clear();
     ready_queue.clear();
 }
 
-void ThreadingShutdown() {
-}
-
 } // namespace
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index b364beed9..7b6ab4ce0 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -406,6 +406,9 @@ void Shutdown() {
     lock = nullptr;
     notification_event = nullptr;
     parameter_event = nullptr;
+
+    next_parameter.object = nullptr;
+
     HLE::Applets::Shutdown();
 }
 
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index fafb43a2f..a8cb15d60 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -310,4 +310,9 @@ Interface::Interface() {
     Register(FunctionTable);
 }
 
+Interface::~Interface() {
+    semaphore_event = nullptr;
+    interrupt_event = nullptr;
+}
+
 } // namespace
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index 54109b2a9..b6f611db5 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -16,6 +16,7 @@ namespace DSP_DSP {
 class Interface : public Service::Interface {
 public:
     Interface();
+    ~Interface() override;
 
     std::string GetPortName() const override {
         return "dsp::DSP";
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 3910d0227..8b40ba376 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -584,7 +584,7 @@ const Interface::FunctionInfo FunctionTable[] = {
 Interface::Interface() {
     Register(FunctionTable);
 
-    g_interrupt_event = 0;
+    g_interrupt_event = nullptr;
 
     using Kernel::MemoryPermission;
     g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
@@ -593,4 +593,9 @@ Interface::Interface() {
     g_thread_id = 0;
 }
 
+Interface::~Interface() {
+    g_interrupt_event = nullptr;
+    g_shared_memory = nullptr;
+}
+
 } // namespace
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 268089fdd..c89d0a467 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -161,6 +161,7 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec
 class Interface : public Service::Interface {
 public:
     Interface();
+    ~Interface() override;
 
     std::string GetPortName() const override {
         return "gsp::Gpu";
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index 25b01860e..18b22956f 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -125,4 +125,8 @@ Interface::Interface() {
     Register(FunctionTable);
 }
 
+Interface::~Interface() {
+    handle_event = nullptr;
+}
+
 } // namespace
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h
index 82abdff28..0ced2359c 100644
--- a/src/core/hle/service/nwm_uds.h
+++ b/src/core/hle/service/nwm_uds.h
@@ -16,6 +16,7 @@ namespace NWM_UDS {
 class Interface : public Service::Interface {
 public:
     Interface();
+    ~Interface() override;
 
     std::string GetPortName() const override {
         return "nwm::UDS";
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 6c49fa6cf..3b8c7c0e4 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -68,4 +68,8 @@ Interface::Interface() {
     Register(FunctionTable);
 }
 
+Interface::~Interface() {
+    event_handle = nullptr;
+}
+
 } // namespace
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h
index 653aba5cb..96c89b025 100644
--- a/src/core/hle/service/srv.h
+++ b/src/core/hle/service/srv.h
@@ -13,6 +13,7 @@ namespace SRV {
 class Interface : public Service::Interface {
 public:
     Interface();
+    ~Interface() override;
 
     std::string GetPortName() const override {
         return "srv:";
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index e121a54e3..6e7dafaad 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -410,4 +410,8 @@ Interface::Interface() {
     Register(FunctionTable);
 }
 
+Interface::~Interface() {
+    completion_event = nullptr;
+}
+
 } // namespace
diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h
index 9454e5aab..3965a5545 100644
--- a/src/core/hle/service/y2r_u.h
+++ b/src/core/hle/service/y2r_u.h
@@ -112,6 +112,7 @@ struct ConversionConfiguration {
 class Interface : public Service::Interface {
 public:
     Interface();
+    ~Interface() override;
 
     std::string GetPortName() const override {
         return "y2r:u";