File indexing completed on 2024-04-21 14:52:33

0001 /*
0002     SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "fileindexscheduler.h"
0008 
0009 #include "baloodebug.h"
0010 #include "firstrunindexer.h"
0011 #include "newfileindexer.h"
0012 #include "modifiedfileindexer.h"
0013 #include "xattrindexer.h"
0014 #include "filecontentindexer.h"
0015 #include "unindexedfileindexer.h"
0016 #include "indexcleaner.h"
0017 
0018 #include "fileindexerconfig.h"
0019 
0020 #include <memory>
0021 
0022 #include <QDBusConnection>
0023 
0024 using namespace Baloo;
0025 
0026 FileIndexScheduler::FileIndexScheduler(Database* db, FileIndexerConfig* config, bool firstRun, QObject* parent)
0027     : QObject(parent)
0028     , m_db(db)
0029     , m_config(config)
0030     , m_provider(db)
0031     , m_contentIndexer(nullptr)
0032     , m_indexerState(Startup)
0033     , m_checkUnindexedFiles(false)
0034     , m_checkStaleIndexEntries(false)
0035     , m_isGoingIdle(false)
0036     , m_isSuspended(false)
0037     , m_isFirstRun(firstRun)
0038     , m_inStartup(true)
0039 {
0040     Q_ASSERT(db);
0041     Q_ASSERT(config);
0042 
0043     m_threadPool.setMaxThreadCount(1);
0044 
0045     connect(&m_powerMonitor, &PowerStateMonitor::powerManagementStatusChanged,
0046             this, &FileIndexScheduler::powerManagementStatusChanged);
0047 
0048     if (m_powerMonitor.isOnBattery()) {
0049         m_indexerState = LowPowerIdle;
0050     }
0051 
0052     m_contentIndexer = new FileContentIndexer(m_config->maxUncomittedFiles(), &m_provider, m_indexFinishedFiles, this);
0053     m_contentIndexer->setAutoDelete(false);
0054     connect(m_contentIndexer, &FileContentIndexer::done, this,
0055             &FileIndexScheduler::runnerFinished);
0056     connect(m_contentIndexer, &FileContentIndexer::committedBatch, [this](uint time, uint batchSize) {
0057             this->m_timeEstimator.handleNewBatchTime(time, batchSize);
0058     });
0059 
0060     QDBusConnection::sessionBus().registerObject(QStringLiteral("/scheduler"),
0061                                                  this, QDBusConnection::ExportScriptableContents);
0062 }
0063 
0064 FileIndexScheduler::~FileIndexScheduler()
0065 {
0066     m_contentIndexer->quit();
0067     m_threadPool.waitForDone(0); // wait 0 msecs
0068 }
0069 
0070 void FileIndexScheduler::startupFinished() {
0071     m_inStartup = false;
0072     QTimer::singleShot(0, this, &FileIndexScheduler::scheduleIndexing);
0073 }
0074 
0075 void FileIndexScheduler::scheduleIndexing()
0076 {
0077     if (!isIndexerIdle()) {
0078         return;
0079     }
0080     m_isGoingIdle = false;
0081 
0082     if (m_isSuspended) {
0083         if (m_indexerState != Suspended) {
0084             m_indexerState = Suspended;
0085             Q_EMIT stateChanged(m_indexerState);
0086         }
0087         return;
0088     }
0089 
0090     if (m_isFirstRun) {
0091         if (m_inStartup) {
0092             return;
0093         }
0094 
0095         m_isFirstRun = false;
0096         auto runnable = new FirstRunIndexer(m_db, m_config, m_config->includeFolders());
0097         connect(runnable, &FirstRunIndexer::done, this, &FileIndexScheduler::runnerFinished);
0098 
0099         m_threadPool.start(runnable);
0100         m_indexerState = FirstRun;
0101         Q_EMIT stateChanged(m_indexerState);
0102         return;
0103     }
0104 
0105     if (!m_newFiles.isEmpty()) {
0106         auto runnable = new NewFileIndexer(m_db, m_config, m_newFiles);
0107         connect(runnable, &NewFileIndexer::done, this, &FileIndexScheduler::runnerFinished);
0108 
0109         m_threadPool.start(runnable);
0110         m_newFiles.clear();
0111         m_indexerState = NewFiles;
0112         Q_EMIT stateChanged(m_indexerState);
0113         return;
0114     }
0115 
0116     if (!m_modifiedFiles.isEmpty()) {
0117         auto runnable = new ModifiedFileIndexer(m_db, m_config, m_modifiedFiles);
0118         connect(runnable, &ModifiedFileIndexer::done, this, &FileIndexScheduler::runnerFinished);
0119 
0120         m_threadPool.start(runnable);
0121         m_modifiedFiles.clear();
0122         m_indexerState = ModifiedFiles;
0123         Q_EMIT stateChanged(m_indexerState);
0124         return;
0125     }
0126 
0127     if (!m_xattrFiles.isEmpty()) {
0128         auto runnable = new XAttrIndexer(m_db, m_config, m_xattrFiles);
0129         connect(runnable, &XAttrIndexer::done, this, &FileIndexScheduler::runnerFinished);
0130 
0131         m_threadPool.start(runnable);
0132         m_xattrFiles.clear();
0133         m_indexerState = XAttrFiles;
0134         Q_EMIT stateChanged(m_indexerState);
0135         return;
0136     }
0137 
0138     // No housekeeping, no content indexing
0139     if (m_powerMonitor.isOnBattery()) {
0140         if (m_indexerState != LowPowerIdle) {
0141             m_indexerState = LowPowerIdle;
0142             Q_EMIT stateChanged(m_indexerState);
0143         }
0144         return;
0145     }
0146 
0147     if (m_inStartup) {
0148         if (m_indexerState != Startup) {
0149             m_indexerState = Startup;
0150             Q_EMIT stateChanged(m_indexerState);
0151         }
0152         return;
0153     }
0154 
0155     // This has to be above content indexing, because there can be files that
0156     // should not be indexed in the DB (i.e. if config was changed)
0157     if (m_checkStaleIndexEntries) {
0158         auto runnable = new IndexCleaner(m_db, m_config);
0159         connect(runnable, &IndexCleaner::done, this, &FileIndexScheduler::runnerFinished);
0160 
0161         m_threadPool.start(runnable);
0162         m_checkStaleIndexEntries = false;
0163         m_indexerState = StaleIndexEntriesClean;
0164         Q_EMIT stateChanged(m_indexerState);
0165         return;
0166     }
0167 
0168     m_indexPendingFiles = m_provider.size();
0169     m_indexFinishedFiles = 0;
0170     if (m_indexPendingFiles) {
0171         m_threadPool.start(m_contentIndexer);
0172         m_indexerState = ContentIndexing;
0173         Q_EMIT stateChanged(m_indexerState);
0174         return;
0175     }
0176 
0177     if (m_checkUnindexedFiles) {
0178         auto runnable = new UnindexedFileIndexer(m_db, m_config);
0179         connect(runnable, &UnindexedFileIndexer::done, this, &FileIndexScheduler::runnerFinished);
0180 
0181         m_threadPool.start(runnable);
0182         m_checkUnindexedFiles = false;
0183         m_indexerState = UnindexedFileCheck;
0184         Q_EMIT stateChanged(m_indexerState);
0185         return;
0186     }
0187 
0188     if (m_indexerState != Idle) {
0189         m_indexerState = Idle;
0190         Q_EMIT stateChanged(m_indexerState);
0191     }
0192 }
0193 
0194 static void removeStartsWith(QStringList& list, const QString& dir)
0195 {
0196     const auto tail = std::remove_if(list.begin(), list.end(),
0197         [&dir](const QString& file) {
0198             return file.startsWith(dir);
0199         });
0200     list.erase(tail, list.end());
0201 }
0202 
0203 static void removeShouldNotIndex(QStringList& list, FileIndexerConfig* config)
0204 {
0205     const auto tail = std::remove_if(list.begin(), list.end(),
0206         [config](const QString& file) {
0207             return !config->shouldBeIndexed(file);
0208         });
0209     list.erase(tail, list.end());
0210 }
0211 
0212 void FileIndexScheduler::updateConfig()
0213 {
0214     // Interrupt content indexer, to avoid indexing files that should
0215     // not be indexed (bug 373430)
0216     if (m_indexerState == ContentIndexing) {
0217         m_contentIndexer->quit();
0218     }
0219     removeShouldNotIndex(m_newFiles, m_config);
0220     removeShouldNotIndex(m_modifiedFiles, m_config);
0221     removeShouldNotIndex(m_xattrFiles, m_config);
0222     m_checkStaleIndexEntries = true;
0223     m_checkUnindexedFiles = true;
0224     scheduleIndexing();
0225 }
0226 
0227 void FileIndexScheduler::handleFileRemoved(const QString& file)
0228 {
0229     if (!file.endsWith(QLatin1Char('/'))) {
0230         m_newFiles.removeOne(file);
0231         m_modifiedFiles.removeOne(file);
0232         m_xattrFiles.removeOne(file);
0233     }
0234     else {
0235         removeStartsWith(m_newFiles, file);
0236         removeStartsWith(m_modifiedFiles, file);
0237         removeStartsWith(m_xattrFiles, file);
0238     }
0239 }
0240 
0241 void FileIndexScheduler::powerManagementStatusChanged(bool isOnBattery)
0242 {
0243     qCDebug(BALOO) << "Power state changed - onBattery:" << isOnBattery;
0244     if (isOnBattery && m_indexerState == ContentIndexing) {
0245         qCDebug(BALOO) << "On battery, stopping content indexer";
0246         m_contentIndexer->quit();
0247     } else {
0248         scheduleIndexing();
0249     }
0250 }
0251 
0252 void FileIndexScheduler::setSuspend(bool suspend)
0253 {
0254     m_isSuspended = suspend;
0255     if (suspend) {
0256         qCDebug(BALOO) << "Suspending";
0257         if (m_indexerState == ContentIndexing) {
0258             m_contentIndexer->quit();
0259         } else {
0260             scheduleIndexing();
0261         }
0262     } else {
0263         qCDebug(BALOO) << "Resuming";
0264         // No need to emit here we'll be emitting in scheduling
0265         scheduleIndexing();
0266     }
0267 }
0268 
0269 uint FileIndexScheduler::getRemainingTime()
0270 {
0271     if (m_indexerState != ContentIndexing) {
0272         return 0;
0273     }
0274     uint remainingFiles = m_indexPendingFiles - m_indexFinishedFiles;
0275     return m_timeEstimator.calculateTimeLeft(remainingFiles);
0276 }
0277 
0278 void FileIndexScheduler::scheduleCheckUnindexedFiles()
0279 {
0280     m_checkUnindexedFiles = true;
0281 }
0282 
0283 void FileIndexScheduler::checkUnindexedFiles()
0284 {
0285     m_checkUnindexedFiles = true;
0286     scheduleIndexing();
0287 }
0288 
0289 void FileIndexScheduler::scheduleCheckStaleIndexEntries()
0290 {
0291     m_checkStaleIndexEntries = true;
0292 }
0293 
0294 void FileIndexScheduler::checkStaleIndexEntries()
0295 {
0296     m_checkStaleIndexEntries = true;
0297     scheduleIndexing();
0298 }
0299 
0300 uint FileIndexScheduler::getBatchSize()
0301 {
0302     return m_config->maxUncomittedFiles();
0303 }
0304 
0305 #include "moc_fileindexscheduler.cpp"