From: Peter Penz Date: Wed, 24 Mar 2010 22:21:09 +0000 (+0000) Subject: Version control: Move the maintainance of pending threads into a custom class. Also... X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/884e95cc985ccf3a7d9de9ebe0d9b1bbd2d9e706?ds=sidebyside Version control: Move the maintainance of pending threads into a custom class. Also the UpdateItemStatesThread differs now between protecting the globally shared plugin and the locally shared data. svn path=/trunk/KDE/kdebase/apps/; revision=1107125 --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0340a9856..c56140e5c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 index 000000000..f61c79975 --- /dev/null +++ b/src/versioncontrol/pendingthreadsmaintainer.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * 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 +#include +#include + +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::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 index 000000000..342cb421d --- /dev/null +++ b/src/versioncontrol/pendingthreadsmaintainer.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * 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 + +#include + +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 m_threads; + QTimer* m_timer; + + friend class PendingThreadsMaintainerSingleton; +}; + +#endif diff --git a/src/versioncontrol/updateitemstatesthread.cpp b/src/versioncontrol/updateitemstatesthread.cpp index 0cd5b93e1..020cdb0d0 100644 --- a/src/versioncontrol/updateitemstatesthread.cpp +++ b/src/versioncontrol/updateitemstatesthread.cpp @@ -19,17 +19,21 @@ #include "updateitemstatesthread.h" +#include + 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& 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 UpdateItemStatesThread::itemStates() const { + QMutexLocker locker(&m_itemMutex); return m_itemStates; } bool UpdateItemStatesThread::retrievedItems() const { + QMutexLocker locker(&m_itemMutex); return m_retrievedItems; } diff --git a/src/versioncontrol/updateitemstatesthread.h b/src/versioncontrol/updateitemstatesthread.h index a687827c9..f99188014 100644 --- a/src/versioncontrol/updateitemstatesthread.h +++ b/src/versioncontrol/updateitemstatesthread.h @@ -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 m_itemStates; }; diff --git a/src/versioncontrol/versioncontrolobserver.cpp b/src/versioncontrol/versioncontrolobserver.cpp index aa78e7e4f..cab62be39 100644 --- a/src/versioncontrol/versioncontrolobserver.cpp +++ b/src/versioncontrol/versioncontrolobserver.cpp @@ -28,6 +28,7 @@ #include #include +#include "pendingthreadsmaintainer.h" #include "updateitemstatesthread.h" #include @@ -35,18 +36,6 @@ #include #include -/* - * 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 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::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;