Baloo

fileindexscheduler.cpp
1/*
2 SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
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
24using namespace Baloo;
25
26FileIndexScheduler::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_checkUnindexedFiles(false)
34 , m_checkStaleIndexEntries(false)
35 , m_isGoingIdle(false)
36 , m_isSuspended(false)
37 , m_isFirstRun(firstRun)
38 , m_inStartup(true)
39{
40 Q_ASSERT(db);
41 Q_ASSERT(config);
42
43 m_threadPool.setMaxThreadCount(1);
44
45 connect(&m_powerMonitor, &PowerStateMonitor::powerManagementStatusChanged,
46 this, &FileIndexScheduler::powerManagementStatusChanged);
47
48 if (m_powerMonitor.isOnBattery()) {
49 m_indexerState = LowPowerIdle;
50 }
51
52 m_contentIndexer = new FileContentIndexer(m_config->maxUncomittedFiles(), &m_provider, m_indexFinishedFiles, this);
53 m_contentIndexer->setAutoDelete(false);
54 connect(m_contentIndexer, &FileContentIndexer::done, this,
55 &FileIndexScheduler::runnerFinished);
56 connect(m_contentIndexer, &FileContentIndexer::committedBatch, [this](uint time, uint batchSize) {
57 this->m_timeEstimator.handleNewBatchTime(time, batchSize);
58 });
59
60 QDBusConnection::sessionBus().registerObject(QStringLiteral("/scheduler"),
62}
63
64FileIndexScheduler::~FileIndexScheduler()
65{
66 m_contentIndexer->quit();
67 m_threadPool.waitForDone(0); // wait 0 msecs
68}
69
70void FileIndexScheduler::startupFinished() {
71 m_inStartup = false;
72 QTimer::singleShot(0, this, &FileIndexScheduler::scheduleIndexing);
73}
74
75void 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
194static 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
203static 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
212void 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
227void 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
241void 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
252void 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
269uint 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
278void FileIndexScheduler::scheduleCheckUnindexedFiles()
279{
280 m_checkUnindexedFiles = true;
281}
282
283void FileIndexScheduler::checkUnindexedFiles()
284{
285 m_checkUnindexedFiles = true;
286 scheduleIndexing();
287}
288
289void FileIndexScheduler::scheduleCheckStaleIndexEntries()
290{
291 m_checkStaleIndexEntries = true;
292}
293
294void FileIndexScheduler::checkStaleIndexEntries()
295{
296 m_checkStaleIndexEntries = true;
297 scheduleIndexing();
298}
299
300uint FileIndexScheduler::getBatchSize()
301{
302 return m_config->maxUncomittedFiles();
303}
304
305#include "moc_fileindexscheduler.cpp"
Active config class which emits signals if the config was changed, for example if the KCM saved the c...
uint maxUncomittedFiles() const
Returns batch size.
QStringList includeFolders() const
Folders to search for files to index and analyze.
Does not check the folder path or the mtime of the file.
void suspend()
Implements storage for docIds without any associated data Instantiated for:
Definition coding.cpp:11
KIOCORE_EXPORT QStringList list(const QString &fileClass)
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
iterator begin()
void clear()
iterator end()
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
bool removeOne(const AT &t)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void start(Callable &&callableToRun, int priority)
bool waitForDone(int msecs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:16 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.