diff --git a/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/indexedstrategy.cpp b/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/indexedstrategy.cpp index bc498aa6..36bc1a81 100644 --- a/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/indexedstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/indexedstrategy.cpp @@ -52,7 +52,6 @@ void ContentIndexedStrategy::initializeIndexing() void ContentIndexedStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); try { @@ -256,7 +255,7 @@ void ContentIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr bool enableRetrieval = optAPI.isFullTextRetrievalEnabled(); for (int32_t i = 0; i < docsSize; ++i) { - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "Content search cancelled"; break; } @@ -414,7 +413,10 @@ void ContentIndexedStrategy::performContentSearch(const SearchQuery &query) { // RAII 守护类:自动管理取消标志的生命周期 // 构造时设置标志,析构时自动清理(即使发生异常) - SearchCancellationGuard guard(&m_cancelled); + // 若 m_cancelledRef 为空(非正常调用链),使用局部 dummy 标志避免空指针解引用 + std::atomic dummyCancelled { false }; + std::atomic *cancelledFlag = m_cancelledRef ? m_cancelledRef : &dummyCancelled; + SearchCancellationGuard guard(cancelledFlag); try { // 获取索引目录 @@ -458,7 +460,7 @@ void ContentIndexedStrategy::performContentSearch(const SearchQuery &query) Collection scoreDocs; try { // 创建可取消的收集器 - boost::shared_ptr collector = newLucene(&m_cancelled, maxResults); + boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // 执行搜索,使用自定义收集器 qInfo() << "Content search execution start:" << query.keyword(); @@ -470,7 +472,7 @@ void ContentIndexedStrategy::performContentSearch(const SearchQuery &query) << "Total hits:" << collector->getTotalHits() << "Collected:" << scoreDocs.size() << "Keyword:" << query.keyword() - << "Cancelled" << m_cancelled.load(); + << "Cancelled" << (m_cancelledRef ? m_cancelledRef->load() : false); } catch (const SearchCancelledException &e) { qInfo() << "Content search cancelled during execution"; emit searchFinished(m_results); @@ -503,7 +505,8 @@ void ContentIndexedStrategy::performContentSearch(const SearchQuery &query) void ContentIndexedStrategy::cancel() { - m_cancelled.store(true); + if (m_cancelledRef) + m_cancelledRef->store(true); } DFM_SEARCH_END_NS diff --git a/src/dfm-search/dfm-search-lib/core/genericsearchengine.cpp b/src/dfm-search/dfm-search-lib/core/genericsearchengine.cpp index 5d8565c9..46248055 100644 --- a/src/dfm-search/dfm-search-lib/core/genericsearchengine.cpp +++ b/src/dfm-search/dfm-search-lib/core/genericsearchengine.cpp @@ -52,8 +52,6 @@ void GenericSearchEngine::init() // 连接控制信号(主线程 -> 工作线程) connect(this, &GenericSearchEngine::requestSearch, m_worker, &SearchWorker::doSearch); - connect(this, &GenericSearchEngine::requestCancel, - m_worker, &SearchWorker::cancelSearch, Qt::DirectConnection); // 连接结果信号(工作线程 -> 主线程) connect(m_worker, &SearchWorker::resultFound, @@ -66,6 +64,9 @@ void GenericSearchEngine::init() // 设置策略工厂 setupStrategyFactory(); + // 将引擎级取消标志注入 worker,使策略能即时读取取消状态 + m_worker->setEngineCancelledFlag(&m_cancelled); + // 启动工作线程 m_workerThread.start(); } @@ -144,11 +145,9 @@ SearchResultExpected GenericSearchEngine::searchSync(const SearchQuery &query) void GenericSearchEngine::cancel() { + // 设置取消标志,工作线程通过注入的 flag 即时响应 m_cancelled.store(true); - // 发射信号请求工作线程取消搜索 - emit requestCancel(); - // 停止批处理定时器 m_batchTimer.stop(); @@ -218,6 +217,8 @@ void GenericSearchEngine::handleErrorOccurred(const DFMSEARCH::SearchError &erro SearchResultExpected GenericSearchEngine::doSyncSearch(const SearchQuery &query) { + // 重置取消标志,避免上次搜索的取消状态残留(与异步 search() 一致) + m_cancelled.store(false); // 重置同步搜索状态 m_results.clear(); m_lastError = SearchError(SearchErrorCode::Success); @@ -244,7 +245,8 @@ SearchResultExpected GenericSearchEngine::doSyncSearch(const SearchQuery &query) // 检查是否超时 if (!timeoutTimer.isActive()) { - emit requestCancel(); + // 超时:设置取消标志通知工作线程停止 + m_cancelled.store(true); return DUnexpected { SearchError(SearchErrorCode::SearchTimeout) }; } diff --git a/src/dfm-search/dfm-search-lib/core/genericsearchengine.h b/src/dfm-search/dfm-search-lib/core/genericsearchengine.h index f6280be6..ef00f8e0 100644 --- a/src/dfm-search/dfm-search-lib/core/genericsearchengine.h +++ b/src/dfm-search/dfm-search-lib/core/genericsearchengine.h @@ -93,11 +93,6 @@ class GenericSearchEngine : public AbstractSearchEngine const DFMSEARCH::SearchOptions &options, DFMSEARCH::SearchType searchType); - /** - * @brief Internal signal to request worker thread to cancel search - */ - void requestCancel(); - protected: /** * @brief Set up the strategy factory for this search engine diff --git a/src/dfm-search/dfm-search-lib/core/searchstrategy/basesearchstrategy.h b/src/dfm-search/dfm-search-lib/core/searchstrategy/basesearchstrategy.h index 27a4ba30..205e36fd 100644 --- a/src/dfm-search/dfm-search-lib/core/searchstrategy/basesearchstrategy.h +++ b/src/dfm-search/dfm-search-lib/core/searchstrategy/basesearchstrategy.h @@ -5,6 +5,7 @@ #define BASESEARCHSTRATEGY_H #include +#include #include #include #include @@ -53,6 +54,25 @@ class BaseSearchStrategy : public QObject */ virtual void cancel() = 0; + /** + * @brief 注入引擎级取消标志指针(必须注入,否则取消无效) + * + * 策略只读取引擎的 atomic flag:主线程调 GenericSearchEngine::cancel() + * 设 true,工作线程即时响应。flag 必须非空,由 SearchWorker::doSearch + * 创建策略后立即注入。 + * + * 注意:不使用 Q_ASSERT,因为它在 Release 构建中会被移除。 + * 若传入空指针,保持 m_cancelledRef 为 nullptr 并打印警告。 + */ + void setCancelledFlag(std::atomic *flag) + { + if (!flag) { + qWarning("BaseSearchStrategy::setCancelledFlag: flag is null, cancellation will be ignored"); + return; + } + m_cancelledRef = flag; + } + Q_SIGNALS: /** * @brief 找到搜索结果信号 @@ -72,7 +92,7 @@ class BaseSearchStrategy : public QObject protected: SearchOptions m_options; SearchResultList m_results; - std::atomic m_cancelled { false }; + std::atomic *m_cancelledRef { nullptr }; }; DFM_SEARCH_END_NS diff --git a/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.cpp b/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.cpp index 3cb1db6c..38940277 100644 --- a/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.cpp +++ b/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.cpp @@ -43,6 +43,14 @@ void SearchWorker::doSearch(const SearchQuery &query, return; } + // 注入引擎级取消标志,使策略能即时响应取消 + if (!m_engineCancelled) { + qWarning("SearchWorker::doSearch: m_engineCancelled is null, aborting search"); + emit errorOccurred(SearchError(SearchErrorCode::InternalError)); + return; + } + m_strategy->setCancelledFlag(m_engineCancelled); + // 连接信号 connect(m_strategy.get(), &BaseSearchStrategy::resultFound, this, &SearchWorker::resultFound); @@ -55,11 +63,4 @@ void SearchWorker::doSearch(const SearchQuery &query, m_strategy->search(query); } -void SearchWorker::cancelSearch() -{ - if (m_strategy) { - m_strategy->cancel(); - } -} - DFM_SEARCH_END_NS diff --git a/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.h b/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.h index 57ca4c00..7996281c 100644 --- a/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.h +++ b/src/dfm-search/dfm-search-lib/core/searchstrategy/searchworker.h @@ -29,6 +29,8 @@ class SearchWorker : public QObject void setStrategyFactory(std::unique_ptr factory); + void setEngineCancelledFlag(std::atomic *flag) { m_engineCancelled = flag; } + public Q_SLOTS: /** * @brief 执行搜索操作 @@ -37,11 +39,6 @@ public Q_SLOTS: const DFMSEARCH::SearchOptions &options, DFMSEARCH::SearchType searchType); - /** - * @brief 取消搜索操作 - */ - void cancelSearch(); - Q_SIGNALS: /** * @brief 搜索结果信号 @@ -61,6 +58,7 @@ public Q_SLOTS: private: std::unique_ptr m_strategyFactory; std::unique_ptr m_strategy; + std::atomic *m_engineCancelled { nullptr }; }; /** diff --git a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/indexedstrategy.cpp b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/indexedstrategy.cpp index 6746fa2f..d69b9197 100644 --- a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/indexedstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/indexedstrategy.cpp @@ -286,7 +286,6 @@ void FileNameIndexedStrategy::initializeIndexing() void FileNameIndexedStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); if (!QFileInfo::exists(m_indexDir)) { @@ -499,7 +498,7 @@ void FileNameIndexedStrategy::executeIndexQuery(const IndexQuery &query, const Q Collection scoreDocs; try { // 创建可取消的收集器 - boost::shared_ptr collector = newLucene(&m_cancelled, maxResults); + boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // 执行搜索,使用自定义收集器 searcher->search(luceneQuery, collector); @@ -523,7 +522,7 @@ void FileNameIndexedStrategy::executeIndexQuery(const IndexQuery &query, const Q // 实时处理搜索结果 for (int i = 0; i < docsSize; i++) { - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "Filename search cancelled"; break; } @@ -827,7 +826,8 @@ BooleanQueryPtr FileNameIndexedStrategy::buildBooleanTermsQuery(const IndexQuery void FileNameIndexedStrategy::cancel() { - m_cancelled.store(true); + if (m_cancelledRef) + m_cancelledRef->store(true); } DFM_SEARCH_END_NS diff --git a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp index dd0ff80f..edc68fee 100644 --- a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp +++ b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp @@ -24,7 +24,6 @@ FileNameRealTimeStrategy::~FileNameRealTimeStrategy() = default; void FileNameRealTimeStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); // 从搜索选项获取参数 @@ -65,7 +64,7 @@ void FileNameRealTimeStrategy::search(const SearchQuery &query) int count = 0; QSet visitedDirs; // 防止符号链接循环 - while (!directoryStack.isEmpty() && count < maxResults && !m_cancelled.load()) { + while (!directoryStack.isEmpty() && count < maxResults && !(m_cancelledRef && m_cancelledRef->load())) { // 取出一个目录进行处理 QString currentDir = directoryStack.pop(); @@ -102,7 +101,7 @@ void FileNameRealTimeStrategy::search(const SearchQuery &query) // 处理当前目录中的每个条目 for (const QFileInfo &info : std::as_const(entries)) { - if (m_cancelled.load() || count >= maxResults) { + if ((m_cancelledRef && m_cancelledRef->load()) || count >= maxResults) { break; } @@ -288,7 +287,8 @@ bool FileNameRealTimeStrategy::matchWildcard(const QString &fileName, const QStr void FileNameRealTimeStrategy::cancel() { - m_cancelled.store(true); + if (m_cancelledRef) + m_cancelledRef->store(true); } DFM_SEARCH_END_NS diff --git a/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/indexedstrategy.cpp b/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/indexedstrategy.cpp index e561b2a6..adbf595a 100644 --- a/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/indexedstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/indexedstrategy.cpp @@ -51,7 +51,6 @@ void OcrTextIndexedStrategy::initializeIndexing() void OcrTextIndexedStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); try { @@ -252,7 +251,7 @@ void OcrTextIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr bool enableRetrieval = optAPI.isFullTextRetrievalEnabled(); for (int32_t i = 0; i < docsSize; ++i) { - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "OCR text search cancelled"; break; } @@ -413,7 +412,10 @@ void OcrTextIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr void OcrTextIndexedStrategy::performOcrTextSearch(const SearchQuery &query) { // RAII guard: automatically manage cancellation flag lifecycle - SearchCancellationGuard guard(&m_cancelled); + // If m_cancelledRef is null (abnormal call chain), use a local dummy to avoid null dereference + std::atomic dummyCancelled { false }; + std::atomic *cancelledFlag = m_cancelledRef ? m_cancelledRef : &dummyCancelled; + SearchCancellationGuard guard(cancelledFlag); try { // Get index directory @@ -456,7 +458,7 @@ void OcrTextIndexedStrategy::performOcrTextSearch(const SearchQuery &query) Collection scoreDocs; try { // Create cancellable collector - boost::shared_ptr collector = newLucene(&m_cancelled, maxResults); + boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // Execute search with custom collector qInfo() << "OCR text search execution start:" << query.keyword(); @@ -468,7 +470,7 @@ void OcrTextIndexedStrategy::performOcrTextSearch(const SearchQuery &query) << "Total hits:" << collector->getTotalHits() << "Collected:" << scoreDocs.size() << "Keyword:" << query.keyword() - << "Cancelled" << m_cancelled.load(); + << "Cancelled" << (m_cancelledRef ? m_cancelledRef->load() : false); } catch (const SearchCancelledException &e) { qInfo() << "OCR text search cancelled during execution"; emit searchFinished(m_results); @@ -500,7 +502,8 @@ void OcrTextIndexedStrategy::performOcrTextSearch(const SearchQuery &query) void OcrTextIndexedStrategy::cancel() { - m_cancelled.store(true); + if (m_cancelledRef) + m_cancelledRef->store(true); } DFM_SEARCH_END_NS