From f4ab1279fd55a00e2bede18ec9ad283c668b46bd Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 3 Mar 2021 15:35:34 +0100 Subject: [PATCH] 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 Reviewed-by: Christian Kandeler --- src/plugins/cpptools/stringtable.cpp | 31 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/plugins/cpptools/stringtable.cpp b/src/plugins/cpptools/stringtable.cpp index 5122c3a8a93..7f5d0178a3d 100644 --- a/src/plugins/cpptools/stringtable.cpp +++ b/src/plugins/cpptools/stringtable.cpp @@ -26,9 +26,11 @@ #include "stringtable.h" #include +#include #include #include +#include #include #include #include @@ -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 &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 m_futureSynchronizer; mutable QMutex m_lock; QAtomicInt m_stopGCRequested{false}; @@ -67,15 +65,26 @@ public: QTimer m_gcCountDown; }; +void StringTablePrivate::addFuture(const QFuture &future) +{ + m_futureSynchronizer.addFuture(future); + const QList> 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);