diff options
author | hjk <[email protected]> | 2022-12-02 16:18:02 +0100 |
---|---|---|
committer | hjk <[email protected]> | 2022-12-06 11:40:11 +0000 |
commit | 7a02b39f265f743879832a8ea5ad48767166cb31 (patch) | |
tree | ec84680764867e8c1d238da05f932789446380b1 /src/libs/utils/stringtable.cpp | |
parent | 9d5d350f8f216bdce402b4d5b6eaa1f11005c795 (diff) |
Utils: Promote CppEditor::StringTable
It's useful on a more general level, and it's only useful as
real singleton.
Change-Id: I1da38a8eb6f9332a36261288d1b088ca6204eb7a
Reviewed-by: Jarek Kobus <[email protected]>
Diffstat (limited to 'src/libs/utils/stringtable.cpp')
-rw-r--r-- | src/libs/utils/stringtable.cpp | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/libs/utils/stringtable.cpp b/src/libs/utils/stringtable.cpp new file mode 100644 index 00000000000..c457251c3ef --- /dev/null +++ b/src/libs/utils/stringtable.cpp @@ -0,0 +1,153 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "stringtable.h" + +#include "runextensions.h" + +#include <QDebug> +#include <QElapsedTimer> +#include <QMutex> +#include <QSet> +#include <QTimer> + +// FIXME: Re-create in better location? +//#ifdef WITH_TESTS +//#include <extensionsystem/pluginmanager.h> +//#endif + +namespace Utils::StringTable { + +enum { + GCTimeOut = 10 * 1000 // 10 seconds +}; + +enum { + DebugStringTable = 1 +}; + +class StringTablePrivate : public QObject +{ +public: + StringTablePrivate(); + ~StringTablePrivate() override { cancelAndWait(); } + + void cancelAndWait(); + QString insert(const QString &string); + void startGC(); + void GC(QFutureInterface<void> &futureInterface); + + QFuture<void> m_future; + QMutex m_lock; + QSet<QString> m_strings; + QTimer m_gcCountDown; +}; + +static StringTablePrivate &stringTable() +{ + static StringTablePrivate theStringTable; + return theStringTable; +} + +StringTablePrivate::StringTablePrivate() +{ + m_strings.reserve(1000); + + m_gcCountDown.setObjectName(QLatin1String("StringTable::m_gcCountDown")); + m_gcCountDown.setSingleShot(true); + m_gcCountDown.setInterval(GCTimeOut); + connect(&m_gcCountDown, &QTimer::timeout, this, &StringTablePrivate::startGC); +} + +QTCREATOR_UTILS_EXPORT QString insert(const QString &string) +{ + return stringTable().insert(string); +} + +void StringTablePrivate::cancelAndWait() +{ + if (!m_future.isRunning()) + return; + m_future.cancel(); + m_future.waitForFinished(); +} + +QString StringTablePrivate::insert(const QString &string) +{ + if (string.isEmpty()) + return string; + + QMutexLocker locker(&m_lock); + // From this point of time any possible new call to startGC() will be held until + // we finish this function. So we are sure that after canceling the running GC() method now, + // no new call to GC() will be executed until we finish this function. + cancelAndWait(); + // A possibly running GC() thread already finished, so it's safe to modify m_strings from + // now until we unlock the mutex. + return *m_strings.insert(string); +} + +void StringTablePrivate::startGC() +{ + QMutexLocker locker(&m_lock); + cancelAndWait(); + m_future = Utils::runAsync(&StringTablePrivate::GC, this); +} + +QTCREATOR_UTILS_EXPORT void scheduleGC() +{ + QMetaObject::invokeMethod(&stringTable().m_gcCountDown, QOverload<>::of(&QTimer::start), + Qt::QueuedConnection); +} + +static int bytesSaved = 0; + +static inline bool isQStringInUse(const QString &string) +{ + QStringPrivate data_ptr = const_cast<QString&>(string).data_ptr(); + if (DebugStringTable) { + const int ref = data_ptr->d_ptr()->ref_; + bytesSaved += (ref - 1) * string.size(); + if (ref > 10) + qDebug() << ref << string.size() << string.left(50); + } + return data_ptr->isShared() || !data_ptr->isMutable() /* QStringLiteral ? */; +} + +void StringTablePrivate::GC(QFutureInterface<void> &futureInterface) +{ +//#ifdef WITH_TESTS +// if (ExtensionSystem::PluginManager::isScenarioRunning("TestStringTable")) { +// if (ExtensionSystem::PluginManager::finishScenario()) +// QThread::sleep(5); +// } +//#endif + + int initialSize = 0; + bytesSaved = 0; + QElapsedTimer timer; + if (DebugStringTable) { + initialSize = m_strings.size(); + timer.start(); + } + + // Collect all QStrings which have refcount 1. (One reference in m_strings and nowhere else.) + for (QSet<QString>::iterator i = m_strings.begin(); i != m_strings.end();) { + if (futureInterface.isCanceled()) + return; + + if (!isQStringInUse(*i)) + i = m_strings.erase(i); + else + ++i; + } + + if (DebugStringTable) { + const int currentSize = m_strings.size(); + qDebug() << "StringTable::GC removed" << initialSize - currentSize + << "strings in" << timer.elapsed() << "ms, size is now" << currentSize + << "saved: " << bytesSaved << "bytes"; + } +} + +} // Utils::StringTable |