Commit ac616bd0 authored by Harald Sitter's avatar Harald Sitter 🏳️‍🌈
Browse files

refcount and delete KDirWatchPrivate instances

this adds reference counting for frontend users of KDirWatchPrivate
instances. it allows us to clean up the thread-local private object when
no more frontend objects are left.

this enables heavily threaded applications to release inotify instances
for threads that aren't deleted but instead returned to a pool.

BUG: 423928
FIXED-IN: 5.75
parent 76744936
......@@ -8,6 +8,8 @@ if(NOT Qt5Test_FOUND)
return()
endif()
find_package(Threads REQUIRED) # std::thread
if(NOT CMAKE_BUILD_TYPE MATCHES "[Dd]ebug$")
set(ENABLE_BENCHMARKS 1)
endif()
......@@ -112,7 +114,7 @@ foreach(_backendName ${KDIRWATCH_BACKENDS_TO_TEST})
string(TOLOWER ${_backendName} _lowercaseBackendName)
set(BACKEND_TEST_TARGET kdirwatch_${_lowercaseBackendName}_unittest)
add_executable(${BACKEND_TEST_TARGET} kdirwatch_unittest.cpp)
target_link_libraries(${BACKEND_TEST_TARGET} Qt5::Test KF5::CoreAddons)
target_link_libraries(${BACKEND_TEST_TARGET} Qt5::Test KF5::CoreAddons Threads::Threads)
ecm_mark_as_test(${BACKEND_TEST_TARGET})
add_test(NAME ${BACKEND_TEST_TARGET} COMMAND ${BACKEND_TEST_TARGET})
target_compile_definitions(${BACKEND_TEST_TARGET} PUBLIC -DKDIRWATCH_TEST_METHOD=\"${_backendName}\")
......
......@@ -14,6 +14,7 @@
#include <QTemporaryDir>
#include <QTest>
#include <QSignalSpy>
#include <thread>
#include <sys/stat.h>
#ifdef Q_OS_UNIX
#include <unistd.h> // ::link()
......@@ -111,6 +112,7 @@ private Q_SLOTS: // test methods
void benchCreateTree();
void benchCreateWatcher();
void benchNotifyWatcher();
void testRefcounting();
protected Q_SLOTS: // internal slots
void nestedEventLoopSlot();
......@@ -905,4 +907,22 @@ void KDirWatch_UnitTest::benchNotifyWatcher()
}
}
void KDirWatch_UnitTest::testRefcounting()
{
bool initialExists = false;
bool secondExists = true; // the expectation is it will be set false
auto thread = std::thread([&] {
QTemporaryDir dir;
{
KDirWatch watch;
watch.addFile(dir.path());
initialExists = KDirWatch::exists();
} // out of scope, the internal private should have been unset
secondExists = KDirWatch::exists();
});
thread.join();
QVERIFY(initialExists);
QVERIFY(!secondExists);
}
#include "kdirwatch_unittest.moc"
......@@ -88,6 +88,11 @@ static KDirWatchPrivate *createPrivate()
}
return dwp_self.localData();
}
static void destroyPrivate()
{
dwp_self.localData()->deleteLater();
dwp_self.setLocalData(nullptr);
}
// Convert a string into a watch Method
static KDirWatch::Method methodFromString(const QByteArray &method)
......@@ -167,7 +172,8 @@ KDirWatchPrivate::KDirWatchPrivate()
#if HAVE_SYS_INOTIFY_H
mSn(nullptr),
#endif
_isStopped(false)
_isStopped(false),
m_references(0)
{
// Debug unittest on CI
if (qAppName() == QLatin1String("kservicetest") || qAppName() == QLatin1String("filetypestest")) {
......@@ -1585,6 +1591,19 @@ bool KDirWatchPrivate::isNoisyFile(const char *filename)
return false;
}
void KDirWatchPrivate::ref()
{
++m_references;
}
void KDirWatchPrivate::unref()
{
--m_references;
if (m_references == 0) {
destroyPrivate();
}
}
#if HAVE_FAM
void KDirWatchPrivate::famEventReceived()
{
......@@ -1873,6 +1892,7 @@ static void postRoutine_KDirWatch()
KDirWatch::KDirWatch(QObject *parent)
: QObject(parent), d(createPrivate())
{
d->ref();
static QBasicAtomicInt nameCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
const int counter = nameCounter.fetchAndAddRelaxed(1); // returns the old value
setObjectName(QStringLiteral("KDirWatch-%1").arg(counter));
......@@ -1887,6 +1907,7 @@ KDirWatch::~KDirWatch()
{
if (d && dwp_self.hasLocalData()) { // skip this after app destruction
d->removeEntries(this);
d->unref();
}
}
......
......@@ -5,6 +5,7 @@
SPDX-FileCopyrightText: 2006 Dirk Mueller <mueller@kde.org>
SPDX-FileCopyrightText: 2007 Flavio Castelli <flavio.castelli@gmail.com>
SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org>
SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: LGPL-2.0-only
......@@ -173,6 +174,9 @@ public:
static bool isNoisyFile(const char *filename);
void ref();
void unref();
public Q_SLOTS:
void slotRescan();
void famEventReceived(); // for FAM
......@@ -221,6 +225,10 @@ public:
#endif
bool _isStopped;
private:
// Public objects that reference this thread-local private instance.
uint m_references;
};
QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment