]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Version control: Move the maintainance of pending threads into a custom class. Also...
authorPeter Penz <peter.penz19@gmail.com>
Wed, 24 Mar 2010 22:21:09 +0000 (22:21 +0000)
committerPeter Penz <peter.penz19@gmail.com>
Wed, 24 Mar 2010 22:21:09 +0000 (22:21 +0000)
svn path=/trunk/KDE/kdebase/apps/; revision=1107125

src/CMakeLists.txt
src/versioncontrol/pendingthreadsmaintainer.cpp [new file with mode: 0644]
src/versioncontrol/pendingthreadsmaintainer.h [new file with mode: 0644]
src/versioncontrol/updateitemstatesthread.cpp
src/versioncontrol/updateitemstatesthread.h
src/versioncontrol/versioncontrolobserver.cpp

index 0340a9856d3dff74f860ac425a0139e019be2c0c..c56140e5c83546eed1f34d32a6a8a03845b6daa7 100644 (file)
@@ -45,6 +45,7 @@ set(dolphinprivate_LIB_SRCS
     tooltips/ktooltip.cpp
     tooltips/ktooltipwindow.cpp
     tooltips/tooltipmanager.cpp
+    versioncontrol/pendingthreadsmaintainer.cpp
     versioncontrol/updateitemstatesthread.cpp
     versioncontrol/versioncontrolobserver.cpp
     viewextensionsfactory.cpp
diff --git a/src/versioncontrol/pendingthreadsmaintainer.cpp b/src/versioncontrol/pendingthreadsmaintainer.cpp
new file mode 100644 (file)
index 0000000..f61c799
--- /dev/null
@@ -0,0 +1,77 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Peter Penz <peter.penz@gmx.at>                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#include "pendingthreadsmaintainer.h"
+
+#include <kglobal.h>
+#include <QThread>
+#include <QTimer>
+
+struct PendingThreadsMaintainerSingleton
+{
+    PendingThreadsMaintainer instance;
+};
+K_GLOBAL_STATIC(PendingThreadsMaintainerSingleton, s_pendingThreadsMaintainer)
+
+
+PendingThreadsMaintainer& PendingThreadsMaintainer::instance()
+{
+    return s_pendingThreadsMaintainer->instance;
+}
+
+PendingThreadsMaintainer::~PendingThreadsMaintainer()
+{
+}
+
+void PendingThreadsMaintainer::append(QThread* thread)
+{
+    Q_ASSERT(thread != 0);
+    m_threads.append(thread);
+    m_timer->start();
+}
+
+PendingThreadsMaintainer::PendingThreadsMaintainer() :
+    QObject(),
+    m_threads(),
+    m_timer(0)
+{
+    m_timer = new QTimer(this);
+    m_timer->setSingleShot(true);
+    m_timer->setInterval(5000); // 5 seconds
+    connect(m_timer, SIGNAL(timeout()), this, SLOT(cleanup()));
+}
+
+void PendingThreadsMaintainer::cleanup()
+{
+    QList<QThread*>::iterator it = m_threads.begin();
+    while (it != m_threads.end()) {
+        if ((*it)->isFinished()) {
+            (*it)->deleteLater();
+            it = m_threads.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    if (!m_threads.isEmpty()) {
+        m_timer->start();
+    }
+}
+
+#include "pendingthreadsmaintainer.moc"
diff --git a/src/versioncontrol/pendingthreadsmaintainer.h b/src/versioncontrol/pendingthreadsmaintainer.h
new file mode 100644 (file)
index 0000000..342cb42
--- /dev/null
@@ -0,0 +1,83 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Peter Penz <peter.penz@gmx.at>                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#ifndef PENDINGTHREADSMAINTAINER_H
+#define PENDINGTHREADSMAINTAINER_H
+
+#include <libdolphin_export.h>
+
+#include <QObject>
+
+class QTimer;
+
+/**
+ * If the creator of a thread gets deleted, although the thread is still
+ * working, usually QThread::wait() is invoked. The drawback of this
+ * approach is that the user interface gets blocked for an undefined amount
+ * of time. If the thread does not contain references to the creator, the
+ * deleting can be forwarded to the PendingThreadsMaintainer. In the following
+ * example it is assumed, that m_thread will be 0, if it has been deleted by the
+ * creator after receiving the signal QThread::finished():
+ *
+ * \code
+ * ThreadCreator::~ThreadCreator()
+ * {
+ *     if (m_thread != 0) {
+ *         PendingThreadsMaintainer::instance().append(m_thread);
+ *         m_thread = 0;
+ *     }
+ * }
+ * \endcode
+ *
+ * The thread will get automatically deleted after it (or has already) been finished.
+ *
+ * Implementation note: Connecting to the signal QThread::finished() is
+ * not sufficient, as it is possible that the thread has already emitted
+ * the signal, but the signal has not been received yet by the thread creator.
+ * Because of this a polling is done each 5 seconds to check, whether the
+ * thread has been finished.
+ */
+class LIBDOLPHINPRIVATE_EXPORT PendingThreadsMaintainer : public QObject
+{
+    Q_OBJECT
+
+public:
+    static PendingThreadsMaintainer& instance();
+    virtual ~PendingThreadsMaintainer();
+
+    /**
+     * Appends the thread \p thread to the maintainer. The thread
+     * will be deleted by the maintainer after it has been finished.
+     */
+    void append(QThread* thread);
+
+protected:
+    PendingThreadsMaintainer();
+
+private slots:
+    void cleanup();
+
+private:
+    QList<QThread*> m_threads;
+    QTimer* m_timer;
+
+    friend class PendingThreadsMaintainerSingleton;
+};
+
+#endif
index 0cd5b93e15ad129d5f4e73a8477d6380bf1234ac..020cdb0d052b83c1545d3796918740888a5e3800 100644 (file)
 
 #include "updateitemstatesthread.h"
 
+#include <QMutexLocker>
+
 UpdateItemStatesThread::UpdateItemStatesThread() :
     QThread(),
-    m_retrievedItems(false),  
-    m_mutex(0),
+    m_globalPluginMutex(0),
+    m_plugin(0),
+    m_itemMutex(),
+    m_retrievedItems(false),
     m_itemStates()
 {
     // Several threads may share one instance of a plugin. A global
     // mutex is required to serialize the retrieval of version control
     // states inside run().
     static QMutex globalMutex;
-    m_mutex = &globalMutex;
+    m_globalPluginMutex = &globalMutex;
 }
 
 UpdateItemStatesThread::~UpdateItemStatesThread()
@@ -39,8 +43,11 @@ UpdateItemStatesThread::~UpdateItemStatesThread()
 void UpdateItemStatesThread::setData(KVersionControlPlugin* plugin,
                                      const QList<VersionControlObserver::ItemState>& itemStates)
 {
-    m_plugin = plugin;
+    QMutexLocker itemLocker(&m_itemMutex);
     m_itemStates = itemStates;
+
+    QMutexLocker pluginLocker(m_globalPluginMutex);
+    m_plugin = plugin;
 }
 
 void UpdateItemStatesThread::run()
@@ -52,11 +59,14 @@ void UpdateItemStatesThread::run()
     // plugin requires the root directory for KVersionControlPlugin::beginRetrieval(). Instead
     // of doing an expensive search, we utilize the knowledge of the implementation of
     // VersionControlObserver::addDirectory() to be sure that the last item contains the root.
+    QMutexLocker itemLocker(&m_itemMutex);
     const QString directory = m_itemStates.last().item.url().directory(KUrl::AppendTrailingSlash);
+    itemLocker.unlock();
 
-    QMutexLocker locker(m_mutex);
+    QMutexLocker pluginLocker(m_globalPluginMutex);
     m_retrievedItems = false;
     if (m_plugin->beginRetrieval(directory)) {
+        itemLocker.relock();
         const int count = m_itemStates.count();
         for (int i = 0; i < count; ++i) {
             m_itemStates[i].version = m_plugin->versionState(m_itemStates[i].item);
@@ -68,21 +78,23 @@ void UpdateItemStatesThread::run()
 
 bool UpdateItemStatesThread::beginReadItemStates()
 {
-    return m_mutex->tryLock(300);
+    return m_itemMutex.tryLock(300);
 }
 
 void UpdateItemStatesThread::endReadItemStates()
 {
-    m_mutex->unlock();
+    m_itemMutex.unlock();
 }
 
 QList<VersionControlObserver::ItemState> UpdateItemStatesThread::itemStates() const
 {
+    QMutexLocker locker(&m_itemMutex);
     return m_itemStates;
 }
 
 bool UpdateItemStatesThread::retrievedItems() const
 {
+    QMutexLocker locker(&m_itemMutex);
     return m_retrievedItems;
 }
 
index a687827c97703368d3b18d8587f8b7206dc24351..f99188014d3db58324ccb7cf0a2937c9ce1e4382 100644 (file)
@@ -54,9 +54,11 @@ protected:
     virtual void run();
 
 private:
-    bool m_retrievedItems;
+    QMutex* m_globalPluginMutex; // Protects the m_plugin globally
     KVersionControlPlugin* m_plugin;
-    QMutex* m_mutex;
+
+    mutable QMutex m_itemMutex; // Protects m_retrievedItems and m_itemStates
+    bool m_retrievedItems;
     QList<VersionControlObserver::ItemState> m_itemStates;
 };
 
index aa78e7e4ffe9b27c3b5911bf13f8f8c8198a2bc1..cab62be39d6b7619a972b2c17a4d8bae339e0a55 100644 (file)
@@ -28,6 +28,7 @@
 #include <kservicetypetrader.h>
 #include <kversioncontrolplugin.h>
 
+#include "pendingthreadsmaintainer.h"
 #include "updateitemstatesthread.h"
 
 #include <QAbstractProxyModel>
 #include <QMutexLocker>
 #include <QTimer>
 
-/*
- * Maintains a list of pending threads, that get regulary checked
- * whether they are finished and hence can get deleted. QThread::wait()
- * is never used to prevent any blocking of the user interface.
- */
-struct PendingThreadsSingleton
-{
-    QList<UpdateItemStatesThread*> list;
-};
-K_GLOBAL_STATIC(PendingThreadsSingleton, s_pendingThreads)
-
-
 VersionControlObserver::VersionControlObserver(QAbstractItemView* view) :
     QObject(view),
     m_pendingItemStatesUpdate(false),
@@ -92,14 +81,11 @@ VersionControlObserver::~VersionControlObserver()
         } else {
             // The version controller gets deleted, while a thread still
             // is working to get the version information. To avoid a blocking
-            // user interface, no waiting for the finished() signal of the thread is
-            // done. Instead the thread will be remembered inside the global
-            // list s_pendingThreads, which will checked regulary. The thread does
-            // not work on shared data that is part of the VersionController instance,
-            // so skipping the waiting is save.
+            // user interface, the thread will be forwarded to the
+            // PendingThreadsMaintainer, which will delete the thread later.
             disconnect(m_updateItemStatesThread, SIGNAL(finished()),
                        this, SLOT(slotThreadFinished()));
-            s_pendingThreads->list.append(m_updateItemStatesThread);
+            PendingThreadsMaintainer::instance().append(m_updateItemStatesThread);
             m_updateItemStatesThread = 0;
         }
     }
@@ -145,19 +131,6 @@ void VersionControlObserver::silentDirectoryVerification()
 
 void VersionControlObserver::verifyDirectory()
 {
-    if (!s_pendingThreads->list.isEmpty()) {
-        // Try to cleanup pending threads (see explanation in destructor)
-        QList<UpdateItemStatesThread*>::iterator it = s_pendingThreads->list.begin();
-        while (it != s_pendingThreads->list.end()) {
-            if ((*it)->isFinished()) {
-                (*it)->deleteLater();
-                it = s_pendingThreads->list.erase(it);
-            } else {
-                ++it;
-            }
-        }
-    }
-
     KUrl versionControlUrl = m_dirLister->url();
     if (!versionControlUrl.isLocalFile()) {
         return;