Fix a possible crash in StringTable on shutdown

The issue is that when a GC() function is being executed
in a separate thread, the main thread may delete
the StringTable (on shutdown) in the same time. The destructor of
StringTable didn't check in any way that the other
thread is still executing GC() method.

In order to fix it we employ runAsync method, returning
the handle to the running task. We store the handle
in futureSynchronizer, and in destructor of StringTablePrivate
we safely wait for all futures to be finished.

Fixes: QTCREATORBUG-25417
Change-Id: I0039d6041276c521c221e8dfc3894e84e47b82a2
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Jarek Kobus
2021-03-03 15:35:34 +01:00
parent 2800f3b51f
commit f4ab1279fd

View File

@@ -26,9 +26,11 @@
#include "stringtable.h"
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QDebug>
#include <QElapsedTimer>
#include <QFutureSynchronizer>
#include <QMutex>
#include <QSet>
#include <QThreadPool>
@@ -48,18 +50,14 @@ class StringTablePrivate : public QObject
{
public:
StringTablePrivate();
~StringTablePrivate() override { m_futureSynchronizer.waitForFinished(); }
QString insert(const QString &string);
void startGC() { QThreadPool::globalInstance()->start(&m_gcRunner); }
void addFuture(const QFuture<void> &future);
void startGC() { addFuture(Utils::runAsync(&StringTablePrivate::GC, this)); }
void GC();
class GCRunner: public QRunnable {
StringTablePrivate &m_stringTable;
public:
explicit GCRunner(StringTablePrivate &stringTable): m_stringTable(stringTable) {}
void run() override { m_stringTable.GC(); }
} m_gcRunner;
QFutureSynchronizer<void> m_futureSynchronizer;
mutable QMutex m_lock;
QAtomicInt m_stopGCRequested{false};
@@ -67,15 +65,26 @@ public:
QTimer m_gcCountDown;
};
void StringTablePrivate::addFuture(const QFuture<void> &future)
{
m_futureSynchronizer.addFuture(future);
const QList<QFuture<void>> futures = m_futureSynchronizer.futures();
const int maxFuturesCount = 10;
if (futures.count() <= maxFuturesCount)
return;
m_futureSynchronizer.clearFutures();
for (const auto &future : futures) {
if (!future.isFinished())
m_futureSynchronizer.addFuture(future);
}
}
static StringTablePrivate *m_instance = nullptr;
StringTablePrivate::StringTablePrivate()
: m_gcRunner(*this)
{
m_strings.reserve(1000);
m_gcRunner.setAutoDelete(false);
m_gcCountDown.setObjectName(QLatin1String("StringTable::m_gcCountDown"));
m_gcCountDown.setSingleShot(true);
m_gcCountDown.setInterval(GCTimeOut);