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 2af7bef6..fba8bb1a 100644 --- a/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/indexedstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/contentsearch/contentstrategies/indexedstrategy.cpp @@ -49,7 +49,6 @@ void ContentIndexedStrategy::initializeIndexing() void ContentIndexedStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); try { @@ -369,7 +368,7 @@ void ContentIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr m_results.reserve(m_results.size() + static_cast(docsSize)); for (int32_t i = 0; i < docsSize; ++i) { - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "Content search cancelled"; break; } @@ -500,9 +499,19 @@ void ContentIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr void ContentIndexedStrategy::performContentSearch(const SearchQuery &query) { + // 防御性空指针拦截:正常流程 m_cancelledRef 由引擎注入,非空; + // 若注入失败(setCancelledFlag 拒绝空指针),此处拦截避免底层 + // SearchCancellationGuard / CancellableCollector 解引用 nullptr 导致 SIGSEGV。 + if (!m_cancelledRef) { + qWarning("ContentIndexedStrategy::performContentSearch: cancellation flag is null, aborting search"); + emit errorOccurred(SearchError(ContentSearchErrorCode::ContentIndexException)); + emit searchFinished(m_results); + return; + } + // RAII 守护类:自动管理取消标志的生命周期 // 构造时设置标志,析构时自动清理(即使发生异常) - SearchCancellationGuard guard(&m_cancelled); + SearchCancellationGuard guard(m_cancelledRef); try { // 获取索引目录 @@ -543,7 +552,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(); @@ -555,7 +564,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); @@ -588,7 +597,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 d1301f38..0119d216 100644 --- a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/indexedstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/indexedstrategy.cpp @@ -227,7 +227,6 @@ void FileNameIndexedStrategy::initializeIndexing() void FileNameIndexedStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); if (!QFileInfo::exists(m_indexDir)) { @@ -438,7 +437,7 @@ void FileNameIndexedStrategy::executeIndexQuery(const IndexQuery &query) Collection scoreDocs; try { // 创建可取消的收集器 - boost::shared_ptr collector = newLucene(&m_cancelled, maxResults); + boost::shared_ptr collector = newLucene(m_cancelledRef, maxResults); // 执行搜索,使用自定义收集器 searcher->search(luceneQuery, collector); @@ -462,7 +461,7 @@ void FileNameIndexedStrategy::executeIndexQuery(const IndexQuery &query) // 实时处理搜索结果 for (int i = 0; i < docsSize; i++) { - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "Filename search cancelled"; break; } @@ -796,7 +795,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 cfa6ef81..6f209118 100644 --- a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp +++ b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp @@ -25,7 +25,6 @@ FileNameRealTimeStrategy::~FileNameRealTimeStrategy() = default; void FileNameRealTimeStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); // 从搜索选项获取参数 @@ -66,7 +65,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(); @@ -103,7 +102,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; } @@ -314,7 +313,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 b2becabd..3f2ab427 100644 --- a/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/indexedstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/ocrtextsearch/ocrtextstrategies/indexedstrategy.cpp @@ -48,7 +48,6 @@ void OcrTextIndexedStrategy::initializeIndexing() void OcrTextIndexedStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); try { @@ -366,7 +365,7 @@ void OcrTextIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr m_results.reserve(m_results.size() + static_cast(docsSize)); for (int32_t i = 0; i < docsSize; ++i) { - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { qInfo() << "OCR text search cancelled"; break; } @@ -510,8 +509,18 @@ void OcrTextIndexedStrategy::processSearchResults(const Lucene::IndexSearcherPtr void OcrTextIndexedStrategy::performOcrTextSearch(const SearchQuery &query) { + // 防御性空指针拦截:正常流程 m_cancelledRef 由引擎注入,非空; + // 若注入失败(setCancelledFlag 拒绝空指针),此处拦截避免底层 + // SearchCancellationGuard / CancellableCollector 解引用 nullptr 导致 SIGSEGV。 + if (!m_cancelledRef) { + qWarning("OcrTextIndexedStrategy::performOcrTextSearch: cancellation flag is null, aborting search"); + emit errorOccurred(SearchError(OcrTextSearchErrorCode::OcrTextIndexException)); + emit searchFinished(m_results); + return; + } + // RAII guard: automatically manage cancellation flag lifecycle - SearchCancellationGuard guard(&m_cancelled); + SearchCancellationGuard guard(m_cancelledRef); try { // Get index directory @@ -551,7 +560,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(); @@ -563,7 +572,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); @@ -595,7 +604,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 diff --git a/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies/recentsearchstrategy.cpp b/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies/recentsearchstrategy.cpp index 3379cae8..8aa452bc 100644 --- a/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies/recentsearchstrategy.cpp +++ b/src/dfm-search/dfm-search-lib/recentsearch/recentstrategies/recentsearchstrategy.cpp @@ -194,7 +194,6 @@ SearchResult RecentSearchStrategy::toSearchResult(const RecentItem &item) const void RecentSearchStrategy::search(const SearchQuery &query) { - m_cancelled.store(false); m_results.clear(); QElapsedTimer timer; @@ -203,7 +202,7 @@ void RecentSearchStrategy::search(const SearchQuery &query) // Step 1: 从 DBus 拉取全部最近使用记录 QList items = fetchRecentItems(); - if (m_cancelled.load()) { + if (m_cancelledRef && m_cancelledRef->load()) { emit searchFinished(m_results); return; } @@ -237,7 +236,7 @@ void RecentSearchStrategy::search(const SearchQuery &query) int count = 0; for (const RecentItem &item : std::as_const(items)) { - if (m_cancelled.load() || count >= maxResults) { + if ((m_cancelledRef && m_cancelledRef->load()) || count >= maxResults) { break; } @@ -255,7 +254,8 @@ void RecentSearchStrategy::search(const SearchQuery &query) void RecentSearchStrategy::cancel() { - m_cancelled.store(true); + if (m_cancelledRef) + m_cancelledRef->store(true); } DFM_SEARCH_END_NS