From 486950c1aa63263c848820e89e378adad11c8145 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 22 Feb 2022 19:49:22 +0800 Subject: [PATCH] Searcher: use async first phase search --- src/search/filesearchengine.cpp | 12 +-- src/search/filesearchengine.h | 11 +-- src/search/searcher.cpp | 157 ++++++++++++++++++-------------- src/search/searcher.h | 10 ++ src/utils/asyncworker.cpp | 37 ++++++++ src/utils/asyncworker.h | 44 +++++++++ src/utils/utils.pri | 2 + src/widgets/searchpanel.cpp | 7 +- 8 files changed, 191 insertions(+), 89 deletions(-) create mode 100644 src/utils/asyncworker.cpp create mode 100644 src/utils/asyncworker.h diff --git a/src/search/filesearchengine.cpp b/src/search/filesearchengine.cpp index 3ff46a25..8c58b73c 100644 --- a/src/search/filesearchengine.cpp +++ b/src/search/filesearchengine.cpp @@ -9,7 +9,7 @@ using namespace vnotex; FileSearchEngineWorker::FileSearchEngineWorker(QObject *p_parent) - : QThread(p_parent) + : AsyncWorker(p_parent) { } @@ -22,16 +22,6 @@ void FileSearchEngineWorker::setData(const QVector &p_ite m_token = p_token; } -void FileSearchEngineWorker::stop() -{ - m_askedToStop.store(1); -} - -bool FileSearchEngineWorker::isAskedToStop() const -{ - return m_askedToStop.load() == 1; -} - void FileSearchEngineWorker::run() { const int c_batchSize = 100; diff --git a/src/search/filesearchengine.h b/src/search/filesearchengine.h index 0cd9072d..22664825 100644 --- a/src/search/filesearchengine.h +++ b/src/search/filesearchengine.h @@ -8,6 +8,8 @@ #include #include +#include + #include "searchtoken.h" #include "searchdata.h" @@ -15,7 +17,7 @@ namespace vnotex { struct SearchResultItem; - class FileSearchEngineWorker : public QThread + class FileSearchEngineWorker : public AsyncWorker { Q_OBJECT friend class FileSearchEngine; @@ -28,9 +30,6 @@ namespace vnotex const QSharedPointer &p_option, const SearchToken &p_token); - public slots: - void stop(); - signals: void resultItemsReady(const QVector> &p_items); @@ -44,10 +43,6 @@ namespace vnotex void processBatchResults(); - bool isAskedToStop() const; - - QAtomicInt m_askedToStop = 0; - QVector m_items; SearchToken m_token; diff --git a/src/search/searcher.cpp b/src/search/searcher.cpp index 72231feb..e272664d 100644 --- a/src/search/searcher.cpp +++ b/src/search/searcher.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "searchresultitem.h" #include "filesearchengine.h" @@ -53,25 +54,31 @@ SearchState Searcher::search(const QSharedPointer &p_option, const emit logRequested(tr("Searching %n buffer(s)", "", p_buffers.size())); - emit progressUpdated(0, p_buffers.size()); - for (int i = 0; i < p_buffers.size(); ++i) { - if (!p_buffers[i]) { - continue; + m_firstPhaseWorker->doWork([this, p_buffers]() { + emit progressUpdated(0, p_buffers.size()); + for (int i = 0; i < p_buffers.size(); ++i) { + if (!p_buffers[i]) { + continue; + } + + if (isAskedToStop()) { + emit finished(SearchState::Stopped); + return; + } + + auto file = p_buffers[i]->getFile(); + if (!firstPhaseSearch(file.data())) { + emit finished(SearchState::Failed); + return; + } + + emit progressUpdated(i + 1, p_buffers.size()); } - if (isAskedToStop()) { - return SearchState::Stopped; - } + emit finished(SearchState::Finished); + }); - auto file = p_buffers[i]->getFile(); - if (!firstPhaseSearch(file.data())) { - return SearchState::Failed; - } - - emit progressUpdated(i + 1, p_buffers.size()); - } - - return SearchState::Finished; + return SearchState::Busy; } SearchState Searcher::search(const QSharedPointer &p_option, Node *p_folder) @@ -88,29 +95,19 @@ SearchState Searcher::search(const QSharedPointer &p_option, Node emit logRequested(tr("Searching folder (%1)").arg(p_folder->getName())); - QVector secondPhaseItems; - if (!firstPhaseSearchFolder(p_folder, secondPhaseItems)) { - return SearchState::Failed; - } - - if (isAskedToStop()) { - return SearchState::Stopped; - } - - if (!secondPhaseItems.isEmpty()) { - // Do second phase search. - if (!secondPhaseSearch(secondPhaseItems)) { - return SearchState::Failed; + m_firstPhaseWorker->doWork([this, p_folder]() { + if (!firstPhaseSearchFolder(p_folder, m_secondPhaseItems)) { + m_secondPhaseItems.clear(); + emit finished(SearchState::Failed); + return; } - if (isAskedToStop()) { - return SearchState::Stopped; + if (m_secondPhaseItems.isEmpty()) { + emit finished(SearchState::Finished); } + }); - return SearchState::Busy; - } - - return SearchState::Finished; + return SearchState::Busy; } SearchState Searcher::search(const QSharedPointer &p_option, const QVector &p_notebooks) @@ -119,41 +116,32 @@ SearchState Searcher::search(const QSharedPointer &p_option, const return SearchState::Failed; } - QVector secondPhaseItems; + m_firstPhaseWorker->doWork([this, p_notebooks]() { + emit progressUpdated(0, p_notebooks.size()); + for (int i = 0; i < p_notebooks.size(); ++i) { + if (isAskedToStop()) { + m_secondPhaseItems.clear(); + emit finished(SearchState::Stopped); + return; + } - emit progressUpdated(0, p_notebooks.size()); - for (int i = 0; i < p_notebooks.size(); ++i) { - if (isAskedToStop()) { - return SearchState::Stopped; + emit logRequested(tr("Searching notebook (%1)").arg(p_notebooks[i]->getName())); + + if (!firstPhaseSearch(p_notebooks[i], m_secondPhaseItems)) { + m_secondPhaseItems.clear(); + emit finished(SearchState::Failed); + return; + } + + emit progressUpdated(i + 1, p_notebooks.size()); } - emit logRequested(tr("Searching notebook (%1)").arg(p_notebooks[i]->getName())); - - if (!firstPhaseSearch(p_notebooks[i], secondPhaseItems)) { - return SearchState::Failed; + if (m_secondPhaseItems.isEmpty()) { + emit finished(SearchState::Finished); } + }); - emit progressUpdated(i + 1, p_notebooks.size()); - } - - if (isAskedToStop()) { - return SearchState::Stopped; - } - - if (!secondPhaseItems.isEmpty()) { - // Do second phase search. - if (!secondPhaseSearch(secondPhaseItems)) { - return SearchState::Failed; - } - - if (isAskedToStop()) { - return SearchState::Stopped; - } - - return SearchState::Busy; - } - - return SearchState::Finished; + return SearchState::Busy; } bool Searcher::prepare(const QSharedPointer &p_option) @@ -172,12 +160,24 @@ bool Searcher::prepare(const QSharedPointer &p_option) m_filePattern = QRegularExpression(QRegularExpression::wildcardToRegularExpression(m_option->m_filePattern), QRegularExpression::CaseInsensitiveOption); } + if (!m_firstPhaseWorker) { + m_firstPhaseWorker = new AsyncWorkerWithFunctor(this); + connect(m_firstPhaseWorker, &AsyncWorkerWithFunctor::finished, + this, &Searcher::doSecondPhaseSearch); + } + + if (m_firstPhaseWorker->isRunning()) { + emit logRequested(tr("Failed to search due to worker is busy")); + return false; + } + + m_secondPhaseItems.clear(); + return true; } bool Searcher::isAskedToStop() const { - QCoreApplication::sendPostedEvents(); return m_askedToStop; } @@ -357,7 +357,7 @@ bool Searcher::firstPhaseSearchFolder(Node *p_node, QVectorisRoot()) { const auto name = p_node->getName(); const auto folderPath = p_node->fetchAbsolutePath(); const auto relativePath = p_node->fetchPath(); @@ -531,3 +531,26 @@ bool Searcher::searchTag(const Node *p_node) const return false; } + +void Searcher::doSecondPhaseSearch() +{ + if (m_secondPhaseItems.isEmpty()) { + return; + } + + if (isAskedToStop()) { + emit finished(SearchState::Stopped); + return; + } + + // Do second phase search. + if (!secondPhaseSearch(m_secondPhaseItems)) { + emit finished(SearchState::Failed); + return; + } + + if (isAskedToStop()) { + emit finished(SearchState::Stopped); + return; + } +} diff --git a/src/search/searcher.h b/src/search/searcher.h index 6e1d4b0f..6355c4f4 100644 --- a/src/search/searcher.h +++ b/src/search/searcher.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "searchdata.h" #include "searchtoken.h" @@ -17,6 +18,7 @@ namespace vnotex struct SearchResultItem; class Node; class Notebook; + class AsyncWorkerWithFunctor; class Searcher : public QObject { @@ -83,6 +85,9 @@ namespace vnotex void createSearchEngine(); + // Will be called after m_firstPhaseWorker finished. + void doSecondPhaseSearch(); + QSharedPointer m_option; SearchToken m_token; @@ -92,6 +97,11 @@ namespace vnotex bool m_askedToStop = false; QScopedPointer m_engine; + + AsyncWorkerWithFunctor *m_firstPhaseWorker = nullptr; + + // Pending second phase items. + QVector m_secondPhaseItems; }; } diff --git a/src/utils/asyncworker.cpp b/src/utils/asyncworker.cpp new file mode 100644 index 00000000..b4b59c6d --- /dev/null +++ b/src/utils/asyncworker.cpp @@ -0,0 +1,37 @@ +#include "asyncworker.h" + +using namespace vnotex; + +AsyncWorker::AsyncWorker(QObject *p_parent) + : QThread(p_parent) +{ +} + +void AsyncWorker::stop() +{ + m_askedToStop.store(1); +} + +bool AsyncWorker::isAskedToStop() const +{ + return m_askedToStop.load() == 1; +} + + +AsyncWorkerWithFunctor::AsyncWorkerWithFunctor(QObject *p_parent) + : QThread(p_parent) +{ +} + +void AsyncWorkerWithFunctor::doWork(const Functor &p_functor) +{ + Q_ASSERT(!isRunning()); + m_functor = p_functor; + start(); +} + +void AsyncWorkerWithFunctor::run() +{ + Q_ASSERT(m_functor); + m_functor(); +} diff --git a/src/utils/asyncworker.h b/src/utils/asyncworker.h new file mode 100644 index 00000000..38bfe90c --- /dev/null +++ b/src/utils/asyncworker.h @@ -0,0 +1,44 @@ +#ifndef ASYNCWORKER_H +#define ASYNCWORKER_H + +#include +#include + +namespace vnotex +{ + class AsyncWorker : public QThread + { + Q_OBJECT + public: + explicit AsyncWorker(QObject *p_parent = nullptr); + + public slots: + void stop(); + + protected: + bool isAskedToStop() const; + + private: + QAtomicInt m_askedToStop = 0; + }; + + + class AsyncWorkerWithFunctor : public QThread + { + Q_OBJECT + public: + typedef std::function Functor; + + explicit AsyncWorkerWithFunctor(QObject *p_parent = nullptr); + + void doWork(const Functor &p_functor); + + protected: + void run() Q_DECL_OVERRIDE; + + private: + Functor m_functor; + }; +} + +#endif // ASYNCWORKER_H diff --git a/src/utils/utils.pri b/src/utils/utils.pri index 9b04e7b9..a072a928 100644 --- a/src/utils/utils.pri +++ b/src/utils/utils.pri @@ -1,6 +1,7 @@ QT += widgets svg SOURCES += \ + $$PWD/asyncworker.cpp \ $$PWD/callbackpool.cpp \ $$PWD/contentmediautils.cpp \ $$PWD/docsutils.cpp \ @@ -18,6 +19,7 @@ SOURCES += \ $$PWD/clipboardutils.cpp HEADERS += \ + $$PWD/asyncworker.h \ $$PWD/callbackpool.h \ $$PWD/contentmediautils.h \ $$PWD/docsutils.h \ diff --git a/src/widgets/searchpanel.cpp b/src/widgets/searchpanel.cpp index 2b33b05c..b57b17d2 100644 --- a/src/widgets/searchpanel.cpp +++ b/src/widgets/searchpanel.cpp @@ -40,6 +40,10 @@ SearchPanel::SearchPanel(const QSharedPointer &p_provider, { qRegisterMetaType>>("QVector>"); + qRegisterMetaType>("QSharedPointer"); + + qRegisterMetaType("SearchState"); + setupUI(); initOptions(); @@ -442,9 +446,6 @@ SearchState SearchPanel::search(const QSharedPointer &p_option) break; } auto folder = m_provider->getCurrentFolder(); - if (folder && (folder->isRoot())) { - folder = nullptr; - } if (!folder) { break; }