From e22e8035c65d06c61073a88f4e33d68d45b38db1 Mon Sep 17 00:00:00 2001
From: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
Date: Fri, 26 Feb 2010 12:53:30 +0100
Subject: [PATCH] make profile cache thread safe

Reviewed-by: thiago
---
 .../qt4projectmanager/qt4projectmanager.pro   |  1 +
 src/shared/proparser/profileevaluator.cpp     | 93 +++++++++++++------
 src/shared/proparser/profileevaluator.h       | 51 ++++++----
 src/shared/proparser/proitems.h               | 20 +++-
 4 files changed, 119 insertions(+), 46 deletions(-)

diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.pro b/src/plugins/qt4projectmanager/qt4projectmanager.pro
index 399ef99b1cc..74aea3cdb5b 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanager.pro
+++ b/src/plugins/qt4projectmanager/qt4projectmanager.pro
@@ -95,6 +95,7 @@ FORMS += makestep.ui \
     wizards/testwizardpage.ui
 RESOURCES += qt4projectmanager.qrc \
     wizards/wizards.qrc
+DEFINES += PROPARSER_THREAD_SAFE
 include(../../shared/proparser/proparser.pri)
 include(qt-s60/qt-s60.pri)
 include(qt-maemo/qt-maemo.pri)
diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp
index 4ad78d02e4c..57ade55616f 100644
--- a/src/shared/proparser/profileevaluator.cpp
+++ b/src/shared/proparser/profileevaluator.cpp
@@ -44,6 +44,9 @@
 #include <QtCore/QString>
 #include <QtCore/QStringList>
 #include <QtCore/QTextStream>
+#ifdef PROPARSER_THREAD_SAFE
+# include <QtCore/QThreadPool>
+#endif
 
 #ifdef Q_OS_UNIX
 #include <unistd.h>
@@ -94,51 +97,42 @@ static void clearFunctions(ProFileEvaluator::FunctionDefs *defs)
 
 ProFileCache::~ProFileCache()
 {
-    foreach (ProFile *pro, parsed_files)
-        if (pro)
-            pro->deref();
+    foreach (const Entry &ent, parsed_files)
+        if (ent.pro)
+            ent.pro->deref();
 }
 
 void ProFileCache::discardFile(const QString &fileName)
 {
-    QHash<QString, ProFile *>::Iterator it = parsed_files.find(fileName);
+#ifdef PROPARSER_THREAD_SAFE
+    QMutexLocker lck(&mutex);
+#endif
+    QHash<QString, Entry>::Iterator it = parsed_files.find(fileName);
     if (it != parsed_files.end()) {
-        if (it.value())
-            it.value()->deref();
+        if (it->pro)
+            it->pro->deref();
         parsed_files.erase(it);
     }
 }
 
 void ProFileCache::discardFiles(const QString &prefix)
 {
-    QHash<QString, ProFile *>::Iterator
+#ifdef PROPARSER_THREAD_SAFE
+    QMutexLocker lck(&mutex);
+#endif
+    QHash<QString, Entry>::Iterator
             it = parsed_files.begin(),
             end = parsed_files.end();
     while (it != end)
         if (it.key().startsWith(prefix)) {
-            if (it.value())
-                it.value()->deref();
+            if (it->pro)
+                it->pro->deref();
             it = parsed_files.erase(it);
         } else {
             ++it;
         }
 }
 
-void ProFileCache::addFile(const QString &fileName, ProFile *pro)
-{
-    parsed_files[fileName] = pro;
-    if (pro)
-        pro->ref();
-}
-
-ProFile *ProFileCache::getFile(const QString &fileName)
-{
-    ProFile *pro = parsed_files.value(fileName);
-    if (pro)
-        pro->ref();
-    return pro;
-}
-
 ///////////////////////////////////////////////////////////////////////
 //
 // ProFileOption
@@ -3027,14 +3021,59 @@ ProFile *ProFileEvaluator::Private::parsedProFile(const QString &fileName, bool
                                                   const QString &contents)
 {
     ProFile *pro;
-    if (!m_option->cache || !(pro = m_option->cache->getFile(fileName))) {
+    if (cache && m_option->cache) {
+        ProFileCache::Entry *ent;
+#ifdef PROPARSER_THREAD_SAFE
+        QMutexLocker locker(&m_option->cache->mutex);
+#endif
+        QHash<QString, ProFileCache::Entry>::Iterator it =
+                m_option->cache->parsed_files.find(fileName);
+        if (it != m_option->cache->parsed_files.end()) {
+            ent = &*it;
+#ifdef PROPARSER_THREAD_SAFE
+            if (ent->locker) {
+                ++ent->locker->waiters;
+                QThreadPool::globalInstance()->releaseThread();
+                ent->locker->cond.wait(locker.mutex());
+                QThreadPool::globalInstance()->reserveThread();
+                if (!--ent->locker->waiters) {
+                    delete ent->locker;
+                    ent->locker = 0;
+                }
+            }
+#endif
+            if ((pro = ent->pro))
+                pro->ref();
+        } else {
+            ent = &m_option->cache->parsed_files[fileName];
+#ifdef PROPARSER_THREAD_SAFE
+            ent->locker = new ProFileCache::Entry::Locker;
+            locker.unlock();
+#endif
+            pro = new ProFile(fileName);
+            if (!(contents.isNull() ? read(pro) : read(pro, contents))) {
+                delete pro;
+                pro = 0;
+            } else {
+                pro->ref();
+            }
+            ent->pro = pro;
+#ifdef PROPARSER_THREAD_SAFE
+            locker.relock();
+            if (ent->locker->waiters) {
+                ent->locker->cond.wakeAll();
+            } else {
+                delete ent->locker;
+                ent->locker = 0;
+            }
+#endif
+        }
+    } else {
         pro = new ProFile(fileName);
         if (!(contents.isNull() ? read(pro) : read(pro, contents))) {
             delete pro;
             pro = 0;
         }
-        if (m_option->cache && cache)
-            m_option->cache->addFile(fileName, pro);
     }
     return pro;
 }
diff --git a/src/shared/proparser/profileevaluator.h b/src/shared/proparser/profileevaluator.h
index 8834d543e8d..d7b6444a7d8 100644
--- a/src/shared/proparser/profileevaluator.h
+++ b/src/shared/proparser/profileevaluator.h
@@ -36,27 +36,15 @@
 #include <QtCore/QHash>
 #include <QtCore/QStringList>
 #include <QtCore/QStack>
+#ifdef PROPARSER_THREAD_SAFE
+# include <QtCore/QMutex>
+# include <QtCore/QWaitCondition>
+#endif
 
 QT_BEGIN_NAMESPACE
 
 struct ProFileOption;
 
-class ProFileCache
-{
-public:
-    ProFileCache() {}
-    ~ProFileCache();
-
-    void addFile(const QString &fileName, ProFile *pro);
-    ProFile *getFile(const QString &fileName);
-
-    void discardFile(const QString &fileName);
-    void discardFiles(const QString &prefix);
-
-private:
-    QHash<QString, ProFile *> parsed_files;
-};
-
 class ProFileEvaluator
 {
     class Private;
@@ -118,6 +106,37 @@ private:
     template<typename T> friend class QTypeInfo;
 
     friend struct ProFileOption;
+    friend class ProFileCache;
+};
+
+class ProFileCache
+{
+public:
+    ProFileCache() {}
+    ~ProFileCache();
+
+    void discardFile(const QString &fileName);
+    void discardFiles(const QString &prefix);
+
+private:
+    struct Entry {
+        ProFile *pro;
+#ifdef PROPARSER_THREAD_SAFE
+        struct Locker {
+            Locker() : waiters(0) {}
+            QWaitCondition cond;
+            int waiters;
+        };
+        Locker *locker;
+#endif
+    };
+
+    QHash<QString, Entry> parsed_files;
+#ifdef PROPARSER_THREAD_SAFE
+    QMutex mutex;
+#endif
+
+    friend class ProFileEvaluator::Private;
 };
 
 // This struct is from qmake, but we are not using everything.
diff --git a/src/shared/proparser/proitems.h b/src/shared/proparser/proitems.h
index 46d085f066f..108e1080963 100644
--- a/src/shared/proparser/proitems.h
+++ b/src/shared/proparser/proitems.h
@@ -35,6 +35,20 @@
 
 QT_BEGIN_NAMESPACE
 
+#ifdef PROPARSER_THREAD_SAFE
+typedef QAtomicInt ProItemRefCount;
+#else
+class ProItemRefCount {
+public:
+    ProItemRefCount() : m_cnt(0) {}
+    bool ref() { return ++m_cnt != 0; }
+    bool deref() { return --m_cnt != 0; }
+    ProItemRefCount &operator=(int value) { m_cnt = value; return *this; }
+private:
+    int m_cnt;
+};
+#endif
+
 class ProItem
 {
 public:
@@ -95,13 +109,13 @@ public:
     ProItem *items() const { return m_proitems; }
     ProItem **itemsRef() { return &m_proitems; }
 
-    void ref() { ++m_refCount; }
-    void deref() { if (!--m_refCount) delete this; }
+    void ref() { m_refCount.ref(); }
+    void deref() { if (!m_refCount.deref()) delete this; }
 
 private:
     ProItem *m_proitems;
     int m_blockKind;
-    int m_refCount;
+    ProItemRefCount m_refCount;
 };
 
 class ProVariable : public ProItem
-- 
GitLab