From 1392e44c43a7cfeb9aac1db38681660ed9427290 Mon Sep 17 00:00:00 2001 From: Heysion Yuan Date: Fri, 15 May 2026 17:57:36 +0800 Subject: [PATCH] fix(notification): fix memory leak in NotifyServerApplet destructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed the issue where NotificationManager was not properly destroyed when the worker thread exited. The deleteLater() call was scheduled but never executed because the thread's event loop had already stopped. Added comprehensive unit tests to verify the fix covers all destruction scenarios including worker-only, manager-only, and both present cases. Log: Fix memory leak in NotifyServerApplet destructor fix(notification): 修复 NotifyServerApplet 析构函数中的内存泄漏 修复了当工作线程退出时 NotificationManager 未被正确销毁的问题。 deleteLater() 被调度但从未执行,因为线程的事件循环已经停止。 添加了全面的单元测试来验证修复涵盖了所有销毁场景, 包括仅 worker、仅 manager 和两者都存在的情况。 Log: 修复 NotifyServerApplet 析构函数中的内存泄漏 --- .../server/notifyserverapplet.cpp | 17 ++++- .../server/notifyserverapplet_test.cpp | 74 +++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/panels/notification/server/notifyserverapplet.cpp b/panels/notification/server/notifyserverapplet.cpp index b4de43dfc..ae678101f 100644 --- a/panels/notification/server/notifyserverapplet.cpp +++ b/panels/notification/server/notifyserverapplet.cpp @@ -25,13 +25,22 @@ NotifyServerApplet::NotifyServerApplet(QObject *parent) NotifyServerApplet::~NotifyServerApplet() { qDebug(notifyLog) << "Exit notification server."; - if (m_manager) { - m_manager->deleteLater(); - } if (m_worker) { + if (m_manager) { + // Schedule manager deletion while the worker event loop is still able to process it. + connect(m_worker, &QThread::finished, m_manager, &QObject::deleteLater, Qt::UniqueConnection); + } m_worker->exit(); m_worker->wait(); - m_worker->deleteLater(); + delete m_worker; + m_worker = nullptr; + m_manager = nullptr; + return; + } + + if (m_manager) { + delete m_manager; + m_manager = nullptr; } } diff --git a/tests/panels/notification/server/notifyserverapplet_test.cpp b/tests/panels/notification/server/notifyserverapplet_test.cpp index 9a0463167..11cd91195 100644 --- a/tests/panels/notification/server/notifyserverapplet_test.cpp +++ b/tests/panels/notification/server/notifyserverapplet_test.cpp @@ -5,12 +5,17 @@ #include #include +#include #include +#include +#include #include #include #include +#define private public #include "notifyserverapplet.h" +#undef private #include "notificationmanager.h" using namespace notification; @@ -18,6 +23,22 @@ using ::testing::_; using ::testing::Return; using ::testing::Invoke; +class TrackableNotificationManager : public NotificationManager { + Q_OBJECT +public: + explicit TrackableNotificationManager(QObject *parent = nullptr) + : NotificationManager(parent) {} + + ~TrackableNotificationManager() override + { + ++destroyedCount; + } + + static std::atomic_int destroyedCount; +}; + +std::atomic_int TrackableNotificationManager::destroyedCount = 0; + // Mock class for NotificationManager class MockNotificationManager : public NotificationManager { Q_OBJECT @@ -72,6 +93,59 @@ TEST_F(NotifyServerAppletTest, DestructorTest) { EXPECT_NO_THROW(delete testApplet); } +// Test destructor when only manager exists (no worker thread) +TEST_F(NotifyServerAppletTest, DestructorWithManagerOnlyTest) { + TrackableNotificationManager::destroyedCount = 0; + + auto *testApplet = new NotifyServerApplet(); + auto *manager = new TrackableNotificationManager(); + QPointer managerGuard(manager); + + testApplet->m_manager = manager; + // m_worker remains nullptr + + delete testApplet; + + EXPECT_TRUE(managerGuard.isNull()); + EXPECT_EQ(TrackableNotificationManager::destroyedCount.load(), 1); +} + +// Test destructor when worker exists but manager is null +TEST_F(NotifyServerAppletTest, DestructorWithWorkerOnlyTest) { + auto *testApplet = new NotifyServerApplet(); + auto *worker = new QThread(); + + testApplet->m_worker = worker; + testApplet->m_manager = nullptr; + + worker->start(); + QTRY_VERIFY_WITH_TIMEOUT(worker->isRunning(), 1000); + + EXPECT_NO_THROW(delete testApplet); +} + +TEST_F(NotifyServerAppletTest, DestructorDeletesManagerAfterWorkerThreadExit) { + TrackableNotificationManager::destroyedCount = 0; + + auto *testApplet = new NotifyServerApplet(); + auto *worker = new QThread(); + auto *manager = new TrackableNotificationManager(); + QPointer managerGuard(manager); + + testApplet->m_manager = manager; + testApplet->m_worker = worker; + + manager->moveToThread(worker); + worker->start(); + + QTRY_VERIFY_WITH_TIMEOUT(worker->isRunning(), 1000); + + delete testApplet; + + EXPECT_TRUE(managerGuard.isNull()); + EXPECT_EQ(TrackableNotificationManager::destroyedCount.load(), 1); +} + // Test memory leak detection for destructor with initialized applet TEST_F(NotifyServerAppletTest, DestructorMemoryLeakTest) { // This test verifies that all resources are properly cleaned up