File indexing completed on 2024-03-24 03:54: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"