/* * 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_indexStop(0), m_contentIndexStop(0) { qRegisterMetaType("IndexerState"); qRegisterMetaType("BatchIndexer::WorkMode"); qRegisterMetaType("WorkMode"); qRegisterMetaType("Targets"); m_threadPool.setMaxThreadCount(1); connect(&m_fileWatcher, &FileWatcher::filesUpdate, this, &IndexScheduler::updateIndex); connect(m_config, &FileIndexerConfig::fileIndexEnableStatusChanged, this, &IndexScheduler::fileIndexEnable); connect(m_config, &FileIndexerConfig::contentIndexEnableStatusChanged, this, &IndexScheduler::contentIndexEnable); connect(m_config, &FileIndexerConfig::appendIndexDir, this, &IndexScheduler::addNewPath); connect(m_config, &FileIndexerConfig::removeIndexDir, this, &IndexScheduler::removeIndex); m_state = Startup; BatchIndexer::Targets targets = BatchIndexer::Target::None; if(m_config->isFileIndexEnable()) { targets |= BatchIndexer::Target::Basic; } else { m_indexStop.fetchAndStoreRelaxed(1); } if(m_config->isContentIndexEnable()) { targets |= BatchIndexer::Target::Content; } else { m_contentIndexStop.fetchAndStoreRelaxed(1); } start(targets); } void IndexScheduler::addNewPath(const QString &folders, const QStringList &blackList) { if(m_indexStop.LOAD && m_contentIndexStop.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; m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Updating); } if(m_config->isContentIndexEnable()) { target |= BatchIndexer::Target::Content; m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Updating); } 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_indexStop.LOAD && m_contentIndexStop.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(BatchIndexer::Targets target) { if(target & BatchIndexer::Target::Basic) { m_indexStop.fetchAndStoreRelaxed(1); m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Off); qDebug() << "File index has been stopped."; } if(target & BatchIndexer::Target::Content) { m_contentIndexStop.fetchAndStoreRelaxed(1); m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Off); qDebug() << "File content index has been stopped."; } if(m_indexStop.LOAD && m_contentIndexStop.LOAD) { m_fileWatcher.removeWatch(); m_threadPool.clear(); m_threadPool.waitForDone(-1); m_state = Stop; qDebug() << "Index scheduler has been stopped."; Q_EMIT stateChange(m_state); } } IndexScheduler::IndexerState IndexScheduler::getIndexState() { return m_state; } void IndexScheduler::start(BatchIndexer::Targets target) { qDebug() << "Index scheduler start." << target; //检查是否有任务未完成 BatchIndexer::Targets tmpTargets = BatchIndexer::Target::None; if(target & BatchIndexer::Basic) { if(m_indexFirstRunFinished && m_indexRebuildFinished) { tmpTargets |= BatchIndexer::Target::Basic; } } if(target & BatchIndexer::Content) { if(m_contentIndexFirstRunFinished && m_contentIndexRebuildFinished) { tmpTargets |= BatchIndexer::Target::Content; } } if(tmpTargets == BatchIndexer::Target::None) { qDebug() << "Index scheduler running, start operation ignored." << "FirstRun finished: " << m_indexFirstRunFinished << "Rebuild finished: " << m_indexRebuildFinished << "Content index firstRun finished: " << m_contentIndexFirstRunFinished << "Content index rebuild finished: " << m_contentIndexRebuildFinished; return; } //打开异步控制开关 if(target & BatchIndexer::Basic) { m_indexStop.fetchAndStoreRelaxed(0); } if(target & BatchIndexer::Content) { m_contentIndexStop.fetchAndStoreRelaxed(0); } //将索引调度器状态设置为运行中 m_state = Running; Q_EMIT stateChange(m_state); //检查是否有数据库需要重建并且执行重建 BatchIndexer::Targets rebuiltTarget = checkAndRebuild(tmpTargets); BatchIndexer::WorkMode mode = BatchIndexer::WorkMode::Update; BatchIndexer::Targets startTarget = BatchIndexer::Target::None; //如果数据库被执行过重建,那么跳过增量更新步骤。 if((tmpTargets & BatchIndexer::Target::Basic) && !(rebuiltTarget & BatchIndexer::Target::Basic)) { startTarget |= BatchIndexer::Target::Basic; m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Updating); } if((tmpTargets & BatchIndexer::Target::Content) && !(rebuiltTarget & BatchIndexer::Target::Content)) { startTarget |= BatchIndexer::Target::Content; m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Updating); } startIndexJob(m_config->currentIndexableDir(), m_config->currentBlackListOfIndex(), mode, startTarget); //启动监听 m_fileWatcher.installWatches(); } BatchIndexer::Targets IndexScheduler::checkAndRebuild(BatchIndexer::Targets target) { BatchIndexer::WorkMode mode = BatchIndexer::WorkMode::Rebuild; BatchIndexer::Targets rebuildTarget = BatchIndexer::Target::None; if((target & BatchIndexer::Target::Basic) && m_config->isFileIndexEnable() && (m_statusRecorder->getStatus(INDEX_DATABASE_STATE_KEY).toInt() == IndexStatusRecorder::State::Error || !m_statusRecorder->versionCheck(INDEX_DATABASE_VERSION_KEY, INDEX_DATABASE_VERSION))) { qDebug() << "Basic database need rebuild"; rebuildTarget |= BatchIndexer::Target::Basic; m_statusRecorder->setStatus(INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Initializing); } if((target & BatchIndexer::Target::Content) && m_config->isContentIndexEnable() && (m_statusRecorder->getStatus(CONTENT_INDEX_DATABASE_STATE_KEY).toInt() == IndexStatusRecorder::State::Error || !m_statusRecorder->versionCheck(CONTENT_DATABASE_VERSION_KEY, CONTENT_DATABASE_VERSION))) { qDebug() << "Content database need rebuild"; rebuildTarget |= BatchIndexer::Target::Content; m_statusRecorder->setStatus(CONTENT_INDEX_DATABASE_STATE_KEY, IndexStatusRecorder::State::Initializing); } startIndexJob(m_config->currentIndexableDir(), m_config->currentBlackListOfIndex(), mode, rebuildTarget); return rebuildTarget; } 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_addNewPathFinished = false; break; case BatchIndexer::WorkMode::Rebuild: if(target & BatchIndexer::Basic) { m_indexRebuildFinished = false; } if(target & BatchIndexer::Content) { m_contentIndexRebuildFinished = false; } break; case BatchIndexer::WorkMode::Update: if(target & BatchIndexer::Basic) { m_indexFirstRunFinished = false; } if(target & BatchIndexer::Content) { m_contentIndexFirstRunFinished = false; } break; default: break; } BatchIndexer *indexer = new BatchIndexer(folders, blackList, m_indexStop, m_contentIndexStop, mode, target); connect(indexer, &BatchIndexer::done, this, &IndexScheduler::firstRunFinished, Qt::QueuedConnection); connect(indexer, &BatchIndexer::progress, this, &IndexScheduler::process, Qt::QueuedConnection); connect(indexer, &BatchIndexer::basicIndexDone, this, &IndexScheduler::onBasicIndexDone, Qt::QueuedConnection); connect(indexer, &BatchIndexer::contentIndexDone, this, &IndexScheduler::onContentIndexDone, Qt::QueuedConnection); m_threadPool.start(indexer); } } void IndexScheduler::fileIndexEnable(bool enable) { if(enable) { start(BatchIndexer::Basic); } else { stop(BatchIndexer::Basic); } } void IndexScheduler::contentIndexEnable(bool enable) { if(enable) { start(BatchIndexer::Content); } else { stop(BatchIndexer::Content); } } void IndexScheduler::updateIndex(const QVector &files) { qDebug() << "updateIndex====="; m_updateFinished = false; m_state = Running; IndexUpdater *updateJob = new IndexUpdater(files, m_indexStop, m_contentIndexStop); connect(updateJob, &IndexUpdater::done, this, &IndexScheduler::updateFinished, Qt::QueuedConnection); m_threadPool.start(updateJob); } void IndexScheduler::firstRunFinished() { if(isIdle()) { m_state = Idle; Q_EMIT stateChange(m_state); } } void IndexScheduler::updateFinished() { m_updateFinished = true; if(isIdle()) { m_state = Idle; Q_EMIT stateChange(m_state); } } bool IndexScheduler::isIdle() { return m_indexFirstRunFinished && m_contentIndexFirstRunFinished && m_addNewPathFinished && m_updateFinished && m_indexRebuildFinished && m_contentIndexRebuildFinished; } void IndexScheduler::onBasicIndexDone(BatchIndexer::WorkMode mode) { switch (mode) { case BatchIndexer::WorkMode::Add: m_addNewPathFinished = true; break; case BatchIndexer::WorkMode::Rebuild: m_indexRebuildFinished = true; break; case BatchIndexer::WorkMode::Update: m_indexFirstRunFinished = true; break; default: break; } 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(success); } void IndexScheduler::onContentIndexDone(BatchIndexer::WorkMode mode) { switch (mode) { case BatchIndexer::WorkMode::Add: m_addNewPathFinished = true; break; case BatchIndexer::WorkMode::Rebuild: m_contentIndexRebuildFinished = true; break; case BatchIndexer::WorkMode::Update: m_contentIndexFirstRunFinished = true; break; default: break; } 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(success); }