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