/* * Copyright (C) 2022, KylinSoft Co., Ltd. * * 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 3 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, see . * * Authors: iaom * */ #include "index-scheduler.h" #include "index-updater.h" #include "compatible-define.h" using namespace UkuiSearch; IndexScheduler::IndexScheduler(QObject *parent) : QObject(parent), m_statusRecorder(IndexStatusRecorder::getInstance()), m_config(FileIndexerConfig::getInstance()), m_state(Startup), m_stop(0) { qRegisterMetaType("IndexerState"); qRegisterMetaType("BatchIndexer::WorkMode"); m_threadPool.setMaxThreadCount(1); connect(&m_fileWatcher, &FileWatcher::filesUpdate, this, &IndexScheduler::updateIndex); connect(m_config, &FileIndexerConfig::fileIndexEnableStatusChanged, this, &IndexScheduler::fileIndexEnable); connect(m_config, &FileIndexerConfig::appendIndexDir, this, &IndexScheduler::addNewPath); connect(m_config, &FileIndexerConfig::removeIndexDir, this, &IndexScheduler::removeIndex); m_state = Startup; if(m_config->isFileIndexEnable()) { start(); } else { m_stop.fetchAndStoreRelaxed(1); } } void IndexScheduler::addNewPath(const QString &folders, const QStringList &blackList) { if(m_stop.LOAD) { qDebug() << "Index Scheduler is being stopped, add operation will be executed when started up next time."; return; } m_state = Running; Q_EMIT stateChange(m_state); BatchIndexer::Targets target = BatchIndexer::Target::None; if(m_config->isFileIndexEnable()) { target |= BatchIndexer::Target::Basic; } if(m_config->isContentIndexEnable()) { target |= BatchIndexer::Target::Content; } BatchIndexer::WorkMode mode = BatchIndexer::WorkMode::Add; startIndexJob(QStringList() << folders, blackList, mode, target); if(BatchIndexer::Target::None != target) { m_fileWatcher.addWatch(folders, blackList); } } void IndexScheduler::removeIndex(const QString &folders) { if(m_stop.LOAD) { qDebug() << "Index Scheduler is being stopped, remove operation will be executed when started up next time."; return; } m_fileWatcher.removeWatch(folders, true); } void IndexScheduler::stop() { m_stop.fetchAndStoreRelaxed(1); m_fileWatcher.removeWatch(); m_threadPool.clear(); m_threadPool.waitForDone(-1); m_state = Stop; qDebug() << "Index scheduler has been stopped."; Q_EMIT stateChange(m_state); } void IndexScheduler::start() { qDebug() << "Index scheduler start."; if(!m_isFirstRunFinished || !m_isRebuildFinished) { qDebug() << "Index scheduler running, start operation ignored. FirstRun finished: " << m_isFirstRunFinished << "Rebuild finished: " << m_isRebuildFinished; return; } m_stop.fetchAndStoreRelaxed(0); m_state = Running; Q_EMIT stateChange(m_state); BatchIndexer::Targets rebuiltTarget = checkAndRebuild(); BatchIndexer::WorkMode mode = BatchIndexer::WorkMode::Update; BatchIndexer::Targets target = BatchIndexer::Target::None; //如果数据库被执行过重建,那么跳过增量更新步骤。 if(m_config->isFileIndexEnable() && !(rebuiltTarget & BatchIndexer::Target::Basic)) { target |= BatchIndexer::Target::Basic; m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Updating); } if(m_config->isContentIndexEnable() && !(rebuiltTarget & BatchIndexer::Target::Content)) { target |= BatchIndexer::Target::Content; m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Updating); } startIndexJob(m_config->currentIndexableDir(), m_config->currentBlackListOfIndex(), mode, target); //启动监听 m_fileWatcher.installWatches(); } IndexScheduler::IndexerState IndexScheduler::getIndexState() { return m_state; } BatchIndexer::Targets IndexScheduler::checkAndRebuild() { BatchIndexer::WorkMode mode = BatchIndexer::WorkMode::Rebuild; BatchIndexer::Targets target = BatchIndexer::Target::None; if((m_statusRecorder->getStatus(INDEX_DATABASE_STATE_KEY).toInt() == IndexStatusRecorder::State::Error || !m_statusRecorder->versionCheck(INDEX_DATABASE_VERSION_KEY, INDEX_DATABASE_VERSION)) && m_config->isFileIndexEnable()) { qDebug() << "Basic database need rebuild"; target |= BatchIndexer::Target::Basic; m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Initializing); } if((m_statusRecorder->getStatus(CONTENT_INDEX_DATABASE_STATE_KEY).toInt() == IndexStatusRecorder::State::Error || !m_statusRecorder->versionCheck(CONTENT_DATABASE_VERSION_KEY, CONTENT_DATABASE_VERSION)) && m_config->isFileIndexEnable()) { qDebug() << "Content database need rebuild"; target |= BatchIndexer::Target::Content; m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Initializing); } startIndexJob(m_config->currentIndexableDir(), m_config->currentBlackListOfIndex(), mode, target); return target; } void IndexScheduler::startIndexJob(const QStringList& folders,const QStringList& blackList, BatchIndexer::WorkMode mode, BatchIndexer::Targets target) { if(BatchIndexer::Target::None != target) { switch (mode) { case BatchIndexer::WorkMode::Add: m_isAddNewPathFinished = false; break; case BatchIndexer::WorkMode::Rebuild: m_isRebuildFinished = false; break; case BatchIndexer::WorkMode::Update: m_isFirstRunFinished = false; break; default: break; } BatchIndexer *indexer = new BatchIndexer(folders, blackList, m_stop, mode, target); connect(indexer, &BatchIndexer::done, this, &IndexScheduler::firstRunFinished, Qt::QueuedConnection); connect(indexer, &BatchIndexer::progress, this, &IndexScheduler::process); connect(indexer, &BatchIndexer::basicIndexDone, this, [&](uint size){ bool success = false; if(!(m_statusRecorder->getStatus(INDEX_DATABASE_STATE_KEY).toInt() == IndexStatusRecorder::State::Error)) { m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Ready); success = true; } Q_EMIT basicIndexDone(size, success); }); connect(indexer, &BatchIndexer::contentIndexDone, this, [&](uint size){ bool success = false; if(!(m_statusRecorder->getStatus(CONTENT_INDEX_DATABASE_STATE_KEY).toInt() == IndexStatusRecorder::State::Error)) { m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Ready); success = true; } Q_EMIT contentIndexDone(size, success); }); m_threadPool.start(indexer); } } void IndexScheduler::fileIndexEnable(bool enable) { //Fix me: 快速反复开关会导致反复执行增量更新操作,可优化。 if(enable) { start(); } else { stop(); } } void IndexScheduler::updateIndex(const QVector &files) { qDebug() << "updateIndex====="; m_isUpdateFinished = false; m_state = Running; IndexUpdater *updateJob = new IndexUpdater(files, m_stop); connect(updateJob, &IndexUpdater::done, this, &IndexScheduler::updateFinished, Qt::QueuedConnection); m_threadPool.start(updateJob); } void IndexScheduler::firstRunFinished(BatchIndexer::WorkMode mode) { switch (mode) { case BatchIndexer::WorkMode::Add: m_isAddNewPathFinished = true; break; case BatchIndexer::WorkMode::Rebuild: m_isRebuildFinished = true; break; case BatchIndexer::WorkMode::Update: m_isFirstRunFinished = true; break; default: break; } if(isIdle()) { m_state = Idle; Q_EMIT stateChange(m_state); } } void IndexScheduler::updateFinished() { m_isUpdateFinished = true; if(isIdle()) { m_state = Idle; Q_EMIT stateChange(m_state); } } bool IndexScheduler::isIdle() { return m_isFirstRunFinished && m_isAddNewPathFinished && m_isUpdateFinished && m_isRebuildFinished; }