1 | /* |
2 | SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.1-or-later |
5 | */ |
6 | |
7 | #include "filecontentindexer.h" |
8 | #include "baloodebug.h" |
9 | #include "config.h" |
10 | #include "extractorprocess.h" |
11 | #include "filecontentindexerprovider.h" |
12 | #include "timeestimator.h" |
13 | |
14 | #include <QEventLoop> |
15 | #include <QElapsedTimer> |
16 | #include <QDBusConnection> |
17 | |
18 | using namespace Baloo; |
19 | |
20 | namespace { |
21 | // TODO KF6 -- remove/combine with started/finished DBus signal |
22 | void sendChangedSignal(const QStringList& updatedFiles) |
23 | { |
24 | auto message = QDBusMessage::createSignal(QStringLiteral("/files" ), |
25 | QStringLiteral("org.kde" ), |
26 | QStringLiteral("changed" )); |
27 | message.setArguments({updatedFiles}); |
28 | QDBusConnection::sessionBus().send(message); |
29 | } |
30 | } |
31 | |
32 | FileContentIndexer::FileContentIndexer(uint batchSize, // |
33 | FileContentIndexerProvider *provider, |
34 | TimeEstimator &timeEstimator, |
35 | QObject *parent) |
36 | : QObject(parent) |
37 | , m_batchSize(batchSize) |
38 | , m_provider(provider) |
39 | , m_stop(0) |
40 | , m_timeEstimator(timeEstimator) |
41 | , m_extractorPath(QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/baloo_file_extractor" )) |
42 | { |
43 | Q_ASSERT(provider); |
44 | |
45 | QDBusConnection bus = QDBusConnection::sessionBus(); |
46 | m_monitorWatcher.setConnection(bus); |
47 | m_monitorWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); |
48 | connect(sender: &m_monitorWatcher, signal: &QDBusServiceWatcher::serviceUnregistered, context: this, |
49 | slot: &FileContentIndexer::monitorClosed); |
50 | |
51 | bus.registerObject(QStringLiteral("/fileindexer" ), |
52 | object: this, options: QDBusConnection::ExportScriptableContents); |
53 | } |
54 | |
55 | void FileContentIndexer::run() |
56 | { |
57 | ExtractorProcess process{m_extractorPath}; |
58 | connect(sender: &process, signal: &ExtractorProcess::startedIndexingFile, context: this, slot: &FileContentIndexer::slotStartedIndexingFile); |
59 | connect(sender: &process, signal: &ExtractorProcess::finishedIndexingFile, context: this, slot: &FileContentIndexer::slotFinishedIndexingFile); |
60 | m_stop.storeRelaxed(newValue: false); |
61 | auto batchSize = m_batchSize; |
62 | uint finishedCount = 0; |
63 | uint totalCount = m_provider->size(); |
64 | |
65 | while (true) { |
66 | // |
67 | // WARNING: This will go mad, if the Extractor does not commit after N=m_batchSize files |
68 | // cause then we will keep fetching the same N files again and again. |
69 | // |
70 | QVector<quint64> idList = m_provider->fetch(size: batchSize); |
71 | if (idList.isEmpty() || m_stop.loadRelaxed()) { |
72 | break; |
73 | } |
74 | QStringList updatedFiles; |
75 | updatedFiles.reserve(asize: idList.size()); |
76 | |
77 | QEventLoop loop; |
78 | connect(sender: &process, signal: &ExtractorProcess::done, context: &loop, slot: &QEventLoop::quit); |
79 | |
80 | bool hadErrors = false; |
81 | connect(sender: &process, signal: &ExtractorProcess::failed, context: &loop, slot: [&hadErrors, &loop]() { hadErrors = true; loop.quit(); }); |
82 | |
83 | auto onFileFinished = [&updatedFiles, &finishedCount, &totalCount, this](const QString &filePath, bool updated) { |
84 | finishedCount++; |
85 | m_timeEstimator.setProgress(totalCount - finishedCount); |
86 | if (updated) { |
87 | updatedFiles.append(t: filePath); |
88 | } |
89 | }; |
90 | connect(sender: &process, signal: &ExtractorProcess::finishedIndexingFile, context: &loop, slot&: onFileFinished, type: Qt::DirectConnection); |
91 | |
92 | QElapsedTimer timer; |
93 | timer.start(); |
94 | |
95 | uint batchStartCount = finishedCount; |
96 | process.index(fileIds: idList); |
97 | loop.exec(); |
98 | batchSize = idList.size(); |
99 | |
100 | if (hadErrors && !m_stop.loadRelaxed()) { |
101 | if (batchSize == 1) { |
102 | auto failedId = idList.first(); |
103 | auto ok = m_provider->markFailed(id: failedId); |
104 | if (!ok) { |
105 | qCCritical(BALOO) << "Not able to commit to DB, DB likely is in a bad state. Exiting" ; |
106 | exit(status: 1); |
107 | } |
108 | batchSize = m_batchSize; |
109 | } else { |
110 | batchSize /= 2; |
111 | } |
112 | // reset to old value - nothing committed |
113 | finishedCount = batchStartCount; |
114 | m_timeEstimator.setProgress(totalCount - finishedCount); |
115 | process.start(); |
116 | } else { |
117 | // Notify some metadata may have changed after a batch has been finished and committed |
118 | sendChangedSignal(updatedFiles); |
119 | |
120 | // Update remaining time estimate |
121 | auto elapsed = timer.elapsed(); |
122 | QMetaObject::invokeMethod(object: this, |
123 | function: [this, elapsed, batchSize] { committedBatch(time: elapsed, batchSize); }, |
124 | type: Qt::QueuedConnection); |
125 | } |
126 | } |
127 | QMetaObject::invokeMethod(object: this, function: &FileContentIndexer::done, type: Qt::QueuedConnection); |
128 | } |
129 | |
130 | void FileContentIndexer::slotStartedIndexingFile(const QString& filePath) |
131 | { |
132 | m_currentFile = filePath; |
133 | if (!m_registeredMonitors.isEmpty()) { |
134 | Q_EMIT startedIndexingFile(filePath); |
135 | } |
136 | } |
137 | |
138 | void FileContentIndexer::slotFinishedIndexingFile(const QString &filePath) |
139 | { |
140 | m_currentFile = QString(); |
141 | if (!m_registeredMonitors.isEmpty()) { |
142 | Q_EMIT finishedIndexingFile(filePath); |
143 | } |
144 | } |
145 | |
146 | void FileContentIndexer::registerMonitor(const QDBusMessage& message) |
147 | { |
148 | if (!m_registeredMonitors.contains(str: message.service())) { |
149 | m_registeredMonitors << message.service(); |
150 | m_monitorWatcher.addWatchedService(newService: message.service()); |
151 | } |
152 | } |
153 | |
154 | void FileContentIndexer::unregisterMonitor(const QDBusMessage& message) |
155 | { |
156 | m_registeredMonitors.removeAll(t: message.service()); |
157 | m_monitorWatcher.removeWatchedService(service: message.service()); |
158 | } |
159 | |
160 | void FileContentIndexer::monitorClosed(const QString& service) |
161 | { |
162 | m_registeredMonitors.removeAll(t: service); |
163 | m_monitorWatcher.removeWatchedService(service); |
164 | } |
165 | |
166 | #include "moc_filecontentindexer.cpp" |
167 | |