Baloo

fileindexscheduler.cpp
1 /*
2  SPDX-FileCopyrightText: 2015 Vishesh Handa <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "fileindexscheduler.h"
8 
9 #include "baloodebug.h"
10 #include "firstrunindexer.h"
11 #include "newfileindexer.h"
12 #include "modifiedfileindexer.h"
13 #include "xattrindexer.h"
14 #include "filecontentindexer.h"
15 #include "unindexedfileindexer.h"
16 #include "indexcleaner.h"
17 
18 #include "fileindexerconfig.h"
19 
20 #include <memory>
21 
22 #include <QDBusConnection>
23 
24 using namespace Baloo;
25 
26 FileIndexScheduler::FileIndexScheduler(Database* db, FileIndexerConfig* config, bool firstRun, QObject* parent)
27  : QObject(parent)
28  , m_db(db)
29  , m_config(config)
30  , m_provider(db)
31  , m_contentIndexer(nullptr)
32  , m_indexerState(Startup)
33  , m_timeEstimator(this)
34  , m_checkUnindexedFiles(false)
35  , m_checkStaleIndexEntries(false)
36  , m_isGoingIdle(false)
37  , m_isSuspended(false)
38  , m_isFirstRun(firstRun)
39  , m_inStartup(true)
40 {
41  Q_ASSERT(db);
42  Q_ASSERT(config);
43 
44  m_threadPool.setMaxThreadCount(1);
45 
46  connect(&m_powerMonitor, &PowerStateMonitor::powerManagementStatusChanged,
47  this, &FileIndexScheduler::powerManagementStatusChanged);
48 
49  if (m_powerMonitor.isOnBattery()) {
50  m_indexerState = LowPowerIdle;
51  }
52 
53  m_contentIndexer = new FileContentIndexer(m_config->maxUncomittedFiles(), &m_provider, m_indexFinishedFiles, this);
54  m_contentIndexer->setAutoDelete(false);
55  connect(m_contentIndexer, &FileContentIndexer::done, this,
56  &FileIndexScheduler::runnerFinished);
57  connect(m_contentIndexer, &FileContentIndexer::committedBatch, &m_timeEstimator,
58  &TimeEstimator::handleNewBatchTime);
59 
60  QDBusConnection::sessionBus().registerObject(QStringLiteral("/scheduler"),
62 }
63 
64 FileIndexScheduler::~FileIndexScheduler()
65 {
66  m_contentIndexer->quit();
67  m_threadPool.waitForDone(0); // wait 0 msecs
68 }
69 
70 void FileIndexScheduler::startupFinished() {
71  m_inStartup = false;
72  QTimer::singleShot(0, this, &FileIndexScheduler::scheduleIndexing);
73 }
74 
75 void FileIndexScheduler::scheduleIndexing()
76 {
77  if (!isIndexerIdle()) {
78  return;
79  }
80  m_isGoingIdle = false;
81 
82  if (m_isSuspended) {
83  if (m_indexerState != Suspended) {
84  m_indexerState = Suspended;
85  Q_EMIT stateChanged(m_indexerState);
86  }
87  return;
88  }
89 
90  if (m_isFirstRun) {
91  if (m_inStartup) {
92  return;
93  }
94 
95  m_isFirstRun = false;
96  auto runnable = new FirstRunIndexer(m_db, m_config, m_config->includeFolders());
97  connect(runnable, &FirstRunIndexer::done, this, &FileIndexScheduler::runnerFinished);
98 
99  m_threadPool.start(runnable);
100  m_indexerState = FirstRun;
101  Q_EMIT stateChanged(m_indexerState);
102  return;
103  }
104 
105  if (!m_newFiles.isEmpty()) {
106  auto runnable = new NewFileIndexer(m_db, m_config, m_newFiles);
107  connect(runnable, &NewFileIndexer::done, this, &FileIndexScheduler::runnerFinished);
108 
109  m_threadPool.start(runnable);
110  m_newFiles.clear();
111  m_indexerState = NewFiles;
112  Q_EMIT stateChanged(m_indexerState);
113  return;
114  }
115 
116  if (!m_modifiedFiles.isEmpty()) {
117  auto runnable = new ModifiedFileIndexer(m_db, m_config, m_modifiedFiles);
118  connect(runnable, &ModifiedFileIndexer::done, this, &FileIndexScheduler::runnerFinished);
119 
120  m_threadPool.start(runnable);
121  m_modifiedFiles.clear();
122  m_indexerState = ModifiedFiles;
123  Q_EMIT stateChanged(m_indexerState);
124  return;
125  }
126 
127  if (!m_xattrFiles.isEmpty()) {
128  auto runnable = new XAttrIndexer(m_db, m_config, m_xattrFiles);
129  connect(runnable, &XAttrIndexer::done, this, &FileIndexScheduler::runnerFinished);
130 
131  m_threadPool.start(runnable);
132  m_xattrFiles.clear();
133  m_indexerState = XAttrFiles;
134  Q_EMIT stateChanged(m_indexerState);
135  return;
136  }
137 
138  // No housekeeping, no content indexing
139  if (m_powerMonitor.isOnBattery()) {
140  if (m_indexerState != LowPowerIdle) {
141  m_indexerState = LowPowerIdle;
142  Q_EMIT stateChanged(m_indexerState);
143  }
144  return;
145  }
146 
147  if (m_inStartup) {
148  if (m_indexerState != Startup) {
149  m_indexerState = Startup;
150  Q_EMIT stateChanged(m_indexerState);
151  }
152  return;
153  }
154 
155  // This has to be above content indexing, because there can be files that
156  // should not be indexed in the DB (i.e. if config was changed)
157  if (m_checkStaleIndexEntries) {
158  auto runnable = new IndexCleaner(m_db, m_config);
159  connect(runnable, &IndexCleaner::done, this, &FileIndexScheduler::runnerFinished);
160 
161  m_threadPool.start(runnable);
162  m_checkStaleIndexEntries = false;
163  m_indexerState = StaleIndexEntriesClean;
164  Q_EMIT stateChanged(m_indexerState);
165  return;
166  }
167 
168  m_indexPendingFiles = m_provider.size();
169  m_indexFinishedFiles = 0;
170  if (m_indexPendingFiles) {
171  m_threadPool.start(m_contentIndexer);
172  m_indexerState = ContentIndexing;
173  Q_EMIT stateChanged(m_indexerState);
174  return;
175  }
176 
177  if (m_checkUnindexedFiles) {
178  auto runnable = new UnindexedFileIndexer(m_db, m_config);
179  connect(runnable, &UnindexedFileIndexer::done, this, &FileIndexScheduler::runnerFinished);
180 
181  m_threadPool.start(runnable);
182  m_checkUnindexedFiles = false;
183  m_indexerState = UnindexedFileCheck;
184  Q_EMIT stateChanged(m_indexerState);
185  return;
186  }
187 
188  if (m_indexerState != Idle) {
189  m_indexerState = Idle;
190  Q_EMIT stateChanged(m_indexerState);
191  }
192 }
193 
194 static void removeStartsWith(QStringList& list, const QString& dir)
195 {
196  const auto tail = std::remove_if(list.begin(), list.end(),
197  [&dir](const QString& file) {
198  return file.startsWith(dir);
199  });
200  list.erase(tail, list.end());
201 }
202 
203 static void removeShouldNotIndex(QStringList& list, FileIndexerConfig* config)
204 {
205  const auto tail = std::remove_if(list.begin(), list.end(),
206  [config](const QString& file) {
207  return !config->shouldBeIndexed(file);
208  });
209  list.erase(tail, list.end());
210 }
211 
212 void FileIndexScheduler::updateConfig()
213 {
214  // Interrupt content indexer, to avoid indexing files that should
215  // not be indexed (bug 373430)
216  if (m_indexerState == ContentIndexing) {
217  m_contentIndexer->quit();
218  }
219  removeShouldNotIndex(m_newFiles, m_config);
220  removeShouldNotIndex(m_modifiedFiles, m_config);
221  removeShouldNotIndex(m_xattrFiles, m_config);
222  m_checkStaleIndexEntries = true;
223  m_checkUnindexedFiles = true;
224  scheduleIndexing();
225 }
226 
227 void FileIndexScheduler::handleFileRemoved(const QString& file)
228 {
229  if (!file.endsWith(QLatin1Char('/'))) {
230  m_newFiles.removeOne(file);
231  m_modifiedFiles.removeOne(file);
232  m_xattrFiles.removeOne(file);
233  }
234  else {
235  removeStartsWith(m_newFiles, file);
236  removeStartsWith(m_modifiedFiles, file);
237  removeStartsWith(m_xattrFiles, file);
238  }
239 }
240 
241 void FileIndexScheduler::powerManagementStatusChanged(bool isOnBattery)
242 {
243  qCDebug(BALOO) << "Power state changed - onBattery:" << isOnBattery;
244  if (isOnBattery && m_indexerState == ContentIndexing) {
245  qCDebug(BALOO) << "On battery, stopping content indexer";
246  m_contentIndexer->quit();
247  } else {
248  scheduleIndexing();
249  }
250 }
251 
252 void FileIndexScheduler::setSuspend(bool suspend)
253 {
254  m_isSuspended = suspend;
255  if (suspend) {
256  qCDebug(BALOO) << "Suspending";
257  if (m_indexerState == ContentIndexing) {
258  m_contentIndexer->quit();
259  } else {
260  scheduleIndexing();
261  }
262  } else {
263  qCDebug(BALOO) << "Resuming";
264  // No need to emit here we'll be emitting in scheduling
265  scheduleIndexing();
266  }
267 }
268 
269 uint FileIndexScheduler::getRemainingTime()
270 {
271  if (m_indexerState != ContentIndexing) {
272  return 0;
273  }
274  uint remainingFiles = m_indexPendingFiles - m_indexFinishedFiles;
275  return m_timeEstimator.calculateTimeLeft(remainingFiles);
276 }
277 
278 void FileIndexScheduler::scheduleCheckUnindexedFiles()
279 {
280  m_checkUnindexedFiles = true;
281 }
282 
283 void FileIndexScheduler::checkUnindexedFiles()
284 {
285  m_checkUnindexedFiles = true;
286  scheduleIndexing();
287 }
288 
289 void FileIndexScheduler::scheduleCheckStaleIndexEntries()
290 {
291  m_checkStaleIndexEntries = true;
292 }
293 
294 void FileIndexScheduler::checkStaleIndexEntries()
295 {
296  m_checkStaleIndexEntries = true;
297  scheduleIndexing();
298 }
299 
300 uint FileIndexScheduler::getBatchSize()
301 {
302  return m_config->maxUncomittedFiles();
303 }
304 
305 #include "moc_fileindexscheduler.cpp"
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
Does not check the folder path or the mtime of the file.
bool registerObject(const QString &path, QObject *object, QDBusConnection::RegisterOptions options)
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QDBusConnection sessionBus()
Implements storage for docIds without any associated data Instantiated for:
Definition: coding.cpp:11
Active config class which emits signals if the config was changed, for example if the KCM saved the c...
void suspend()
KSharedConfigPtr config()
QList::iterator erase(QList::iterator pos)
QList::iterator begin()
QList::iterator end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 03:56:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.