فهرست منبع

Merge pull request #7227 from vonchenplus/fix_memory_leak_v2

Fix memory leak v2
bunnei 4 سال پیش
والد
کامیت
b118fa8698

+ 3 - 13
src/core/core.cpp

@@ -83,12 +83,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
     }
 }
 
-void KProcessDeleter(Kernel::KProcess* process) {
-    process->Destroy();
-}
-
-using KProcessPtr = std::unique_ptr<Kernel::KProcess, decltype(&KProcessDeleter)>;
-
 } // Anonymous namespace
 
 FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
@@ -261,11 +255,10 @@ struct System::Impl {
         }
 
         telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
-        main_process = KProcessPtr{Kernel::KProcess::Create(system.Kernel()), KProcessDeleter};
-        ASSERT(Kernel::KProcess::Initialize(main_process.get(), system, "main",
+        auto main_process = Kernel::KProcess::Create(system.Kernel());
+        ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
                                             Kernel::KProcess::ProcessType::Userland)
                    .IsSuccess());
-        main_process->Open();
         const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
         if (load_result != Loader::ResultStatus::Success) {
             LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
@@ -275,7 +268,7 @@ struct System::Impl {
                 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
         }
         AddGlueRegistrationForProcess(*app_loader, *main_process);
-        kernel.MakeCurrentProcess(main_process.get());
+        kernel.MakeCurrentProcess(main_process);
         kernel.InitializeCores();
 
         // Initialize cheat engine
@@ -340,8 +333,6 @@ struct System::Impl {
         kernel.Shutdown();
         memory.Reset();
         applet_manager.ClearAll();
-        // TODO: The main process should be freed based on KAutoObject ref counting.
-        main_process.reset();
 
         LOG_DEBUG(Core, "Shutdown OK");
     }
@@ -403,7 +394,6 @@ struct System::Impl {
     std::unique_ptr<Tegra::GPU> gpu_core;
     std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
     std::unique_ptr<Core::DeviceMemory> device_memory;
-    KProcessPtr main_process{nullptr, KProcessDeleter};
     Core::Memory::Memory memory;
     CpuManager cpu_manager;
     std::atomic_bool is_powered_on{};

+ 1 - 0
src/core/hle/kernel/k_handle_table.cpp

@@ -56,6 +56,7 @@ bool KHandleTable::Remove(Handle handle) {
     }
 
     // Close the object.
+    kernel.UnregisterInUseObject(obj);
     obj->Close();
     return true;
 }

+ 6 - 5
src/core/hle/kernel/k_process.cpp

@@ -434,11 +434,6 @@ void KProcess::PrepareForTermination() {
 }
 
 void KProcess::Finalize() {
-    // Release memory to the resource limit.
-    if (resource_limit != nullptr) {
-        resource_limit->Close();
-    }
-
     // Finalize the handle table and close any open handles.
     handle_table.Finalize();
 
@@ -460,6 +455,12 @@ void KProcess::Finalize() {
         }
     }
 
+    // Release memory to the resource limit.
+    if (resource_limit != nullptr) {
+        resource_limit->Close();
+        resource_limit = nullptr;
+    }
+
     // Perform inherited finalization.
     KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
 }

+ 30 - 6
src/core/hle/kernel/kernel.cpp

@@ -91,12 +91,6 @@ struct KernelCore::Impl {
     }
 
     void Shutdown() {
-        // Shutdown all processes.
-        if (current_process) {
-            current_process->Finalize();
-            current_process->Close();
-            current_process = nullptr;
-        }
         process_list.clear();
 
         // Close all open server ports.
@@ -170,6 +164,24 @@ struct KernelCore::Impl {
         // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
         next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
 
+        // Close kernel objects that were not freed on shutdown
+        {
+            std::lock_guard lk(registered_in_use_objects_lock);
+            if (registered_in_use_objects.size()) {
+                for (auto& object : registered_in_use_objects) {
+                    object->Close();
+                }
+                registered_in_use_objects.clear();
+            }
+        }
+
+        // Shutdown all processes.
+        if (current_process) {
+            current_process->Finalize();
+            current_process->Close();
+            current_process = nullptr;
+        }
+
         // Track kernel objects that were not freed on shutdown
         {
             std::lock_guard lk(registered_objects_lock);
@@ -714,9 +726,11 @@ struct KernelCore::Impl {
     std::unordered_set<KServerPort*> server_ports;
     std::unordered_set<KServerSession*> server_sessions;
     std::unordered_set<KAutoObject*> registered_objects;
+    std::unordered_set<KAutoObject*> registered_in_use_objects;
     std::mutex server_ports_lock;
     std::mutex server_sessions_lock;
     std::mutex registered_objects_lock;
+    std::mutex registered_in_use_objects_lock;
 
     std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
     std::vector<Kernel::PhysicalCore> cores;
@@ -928,6 +942,16 @@ void KernelCore::UnregisterKernelObject(KAutoObject* object) {
     impl->registered_objects.erase(object);
 }
 
+void KernelCore::RegisterInUseObject(KAutoObject* object) {
+    std::lock_guard lk(impl->registered_in_use_objects_lock);
+    impl->registered_in_use_objects.insert(object);
+}
+
+void KernelCore::UnregisterInUseObject(KAutoObject* object) {
+    std::lock_guard lk(impl->registered_in_use_objects_lock);
+    impl->registered_in_use_objects.erase(object);
+}
+
 bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
     return port != impl->named_ports.cend();
 }

+ 8 - 0
src/core/hle/kernel/kernel.h

@@ -204,6 +204,14 @@ public:
     /// destroyed during the current emulation session.
     void UnregisterKernelObject(KAutoObject* object);
 
+    /// Registers kernel objects with guest in use state, this is purely for close
+    /// after emulation has been shutdown.
+    void RegisterInUseObject(KAutoObject* object);
+
+    /// Unregisters a kernel object previously registered with RegisterInUseObject when it was
+    /// destroyed during the current emulation session.
+    void UnregisterInUseObject(KAutoObject* object);
+
     /// Determines whether or not the given port is a valid named port.
     bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
 

+ 6 - 0
src/core/hle/kernel/svc.cpp

@@ -427,11 +427,15 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha
         R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
                                                                          num_handles),
                  ResultInvalidHandle);
+        for (const auto& obj : objs) {
+            kernel.RegisterInUseObject(obj);
+        }
     }
 
     // Ensure handles are closed when we're done.
     SCOPE_EXIT({
         for (u64 i = 0; i < num_handles; ++i) {
+            kernel.UnregisterInUseObject(objs[i]);
             objs[i]->Close();
         }
     });
@@ -1561,6 +1565,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
 
     // If we succeeded, persist a reference to the thread.
     thread->Open();
+    system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
 
     return ResultSuccess;
 }
@@ -1576,6 +1581,7 @@ static void ExitThread(Core::System& system) {
     auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
     system.GlobalSchedulerContext().RemoveThread(current_thread);
     current_thread->Exit();
+    system.Kernel().UnregisterInUseObject(current_thread);
 }
 
 static void ExitThread32(Core::System& system) {