diff --git a/runners/dictionary/dictionarymatchengine.cpp b/runners/dictionary/dictionarymatchengine.cpp index b80a3d942..85346d569 100644 --- a/runners/dictionary/dictionarymatchengine.cpp +++ b/runners/dictionary/dictionarymatchengine.cpp @@ -1,74 +1,80 @@ /* * Copyright (C) 2010, 2012 Jason A. Donenfeld */ #include "dictionarymatchengine.h" #include #include #include #include DictionaryMatchEngine::DictionaryMatchEngine(Plasma::DataEngine *dictionaryEngine, QObject *parent) : QObject(parent), m_dictionaryEngine(dictionaryEngine) { /* We have to connect source in two different places, due to the difference in * how the connection is made based on data availability. There are two cases, * and this extra connection handles the second case. */ + Q_ASSERT(m_dictionaryEngine); connect(m_dictionaryEngine, SIGNAL(sourceAdded(QString)), this, SLOT(sourceAdded(QString))); } /* This function should be called from a different thread. */ QString DictionaryMatchEngine::lookupWord(const QString &word) { if (!m_dictionaryEngine) { qDebug() << "Could not find dictionary data engine."; return QString(); } if (thread() == QThread::currentThread()) { qDebug() << "DictionaryMatchEngine::lookupWord is only meant to be called from non-primary threads."; return QString(); } ThreadData data; m_wordLock.lockForWrite(); m_lockers.insert(word, &data); m_wordLock.unlock(); - /* We lock it in this thread. Then we try to lock it again, which we cannot do, until the other thread - * unlocks it for us first. We time-out after 30 seconds. */ - data.mutex.lock(); QMetaObject::invokeMethod(this, "sourceAdded", Qt::QueuedConnection, Q_ARG(const QString&, QLatin1Char(':') + word)); - if (!data.mutex.tryLock(30 * 1000)) - qDebug() << "The dictionary data engine timed out."; + QMutexLocker locker(&data.mutex); + if (!data.waitCondition.wait(&data.mutex, 30 * 1000)) // Timeout after 30 seconds + qDebug() << "The dictionary data engine timed out (word:" << word << ")"; + locker.unlock(); + + // after a timeout, if dataUpdated gets m_wordLock here, it can lock data->mutex successfully. m_wordLock.lockForWrite(); m_lockers.remove(word, &data); m_wordLock.unlock(); + // after a timeout, if dataUpdated gets m_wordLock here, it won't see this data instance anymore. + + locker.relock(); return data.definition; } void DictionaryMatchEngine::sourceAdded(const QString &source) { m_dictionaryEngine->connectSource(source, this); } void DictionaryMatchEngine::dataUpdated(const QString &source, const Plasma::DataEngine::Data &result) { if (!result.contains(QLatin1String("text"))) return; QString definition(result[QLatin1String("text")].toString()); m_wordLock.lockForRead(); foreach (ThreadData *data, m_lockers.values(source)) { + QMutexLocker locker(&data->mutex); /* Because of QString's CoW semantics, we don't have to worry about * the overhead of assigning this to every item. */ data->definition = definition; - data->mutex.unlock(); + data->waitCondition.notify_one(); } m_wordLock.unlock(); } diff --git a/runners/dictionary/dictionarymatchengine.h b/runners/dictionary/dictionarymatchengine.h index 19ed9710b..c5ce981c3 100644 --- a/runners/dictionary/dictionarymatchengine.h +++ b/runners/dictionary/dictionarymatchengine.h @@ -1,41 +1,43 @@ /* * Copyright (C) 2010, 2012 Jason A. Donenfeld */ #ifndef DICTIONARYMATCHENGINE_H #define DICTIONARYMATCHENGINE_H #include #include #include #include #include +#include namespace Plasma { class RunnerContext; } class DictionaryMatchEngine : public QObject { Q_OBJECT public: DictionaryMatchEngine(Plasma::DataEngine *dictionaryEngine, QObject *parent = 0); QString lookupWord(const QString &word); private: struct ThreadData { + QWaitCondition waitCondition; QMutex mutex; QString definition; }; QMultiMap m_lockers; QReadWriteLock m_wordLock; Plasma::DataEngine *m_dictionaryEngine; private slots: void dataUpdated(const QString &name, const Plasma::DataEngine::Data &data); void sourceAdded(const QString &source); }; #endif