File indexing completed on 2024-04-21 03:51:44

0001 /*
0002     SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "baloodebug.h"
0008 #include "config.h"
0009 #include "filecontentindexer.h"
0010 #include "filecontentindexerprovider.h"
0011 #include "extractorprocess.h"
0012 
0013 #include <QEventLoop>
0014 #include <QElapsedTimer>
0015 #include <QDBusConnection>
0016 
0017 using namespace Baloo;
0018 
0019 namespace {
0020     // TODO KF6 -- remove/combine with started/finished DBus signal
0021     void sendChangedSignal(const QStringList& updatedFiles)
0022     {
0023         auto message = QDBusMessage::createSignal(QStringLiteral("/files"),
0024                                                   QStringLiteral("org.kde"),
0025                                                   QStringLiteral("changed"));
0026         message.setArguments({updatedFiles});
0027         QDBusConnection::sessionBus().send(message);
0028     }
0029 }
0030 
0031 FileContentIndexer::FileContentIndexer(uint batchSize,
0032         FileContentIndexerProvider* provider,
0033         uint& finishedCount, QObject* parent)
0034     : QObject(parent)
0035     , m_batchSize(batchSize)
0036     , m_provider(provider)
0037     , m_finishedCount(finishedCount)
0038     , m_stop(0)
0039     , m_extractorPath(QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/baloo_file_extractor"))
0040 {
0041     Q_ASSERT(provider);
0042 
0043     QDBusConnection bus = QDBusConnection::sessionBus();
0044     m_monitorWatcher.setConnection(bus);
0045     m_monitorWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
0046     connect(&m_monitorWatcher, &QDBusServiceWatcher::serviceUnregistered, this,
0047             &FileContentIndexer::monitorClosed);
0048 
0049     bus.registerObject(QStringLiteral("/fileindexer"),
0050                         this, QDBusConnection::ExportScriptableContents);
0051 }
0052 
0053 void FileContentIndexer::run()
0054 {
0055     ExtractorProcess process{m_extractorPath};
0056     connect(&process, &ExtractorProcess::startedIndexingFile, this, &FileContentIndexer::slotStartedIndexingFile);
0057     connect(&process, &ExtractorProcess::finishedIndexingFile, this, &FileContentIndexer::slotFinishedIndexingFile);
0058     m_stop.storeRelaxed(false);
0059     auto batchSize = m_batchSize;
0060     while (true) {
0061         //
0062         // WARNING: This will go mad, if the Extractor does not commit after N=m_batchSize files
0063         // cause then we will keep fetching the same N files again and again.
0064         //
0065         QVector<quint64> idList = m_provider->fetch(batchSize);
0066         if (idList.isEmpty() || m_stop.loadRelaxed()) {
0067             break;
0068         }
0069         QEventLoop loop;
0070         connect(&process, &ExtractorProcess::done, &loop, &QEventLoop::quit);
0071 
0072         bool hadErrors = false;
0073         connect(&process, &ExtractorProcess::failed, &loop, [&hadErrors, &loop]() { hadErrors = true; loop.quit(); });
0074 
0075         uint batchStartCount = m_finishedCount;
0076         connect(&process, &ExtractorProcess::finishedIndexingFile, &loop, [this]() { m_finishedCount++; });
0077 
0078         QElapsedTimer timer;
0079         timer.start();
0080 
0081         process.index(idList);
0082         loop.exec();
0083         batchSize = idList.size();
0084 
0085         if (hadErrors && !m_stop.loadRelaxed()) {
0086             if (batchSize == 1) {
0087                 auto failedId = idList.first();
0088                 auto ok = m_provider->markFailed(failedId);
0089                 if (!ok) {
0090                     qCCritical(BALOO) << "Not able to commit to DB, DB likely is in a bad state. Exiting";
0091                     exit(1);
0092                 }
0093                 batchSize = m_batchSize;
0094             } else {
0095                 batchSize /= 2;
0096             }
0097             m_updatedFiles.clear();
0098             // reset to old value - nothing committed
0099             m_finishedCount = batchStartCount;
0100             process.start();
0101         } else {
0102             // Notify some metadata may have changed
0103             sendChangedSignal(m_updatedFiles);
0104             m_updatedFiles.clear();
0105 
0106             // Update remaining time estimate
0107             auto elapsed = timer.elapsed();
0108             QMetaObject::invokeMethod(this,
0109                 [this, elapsed, batchSize] { committedBatch(elapsed, batchSize); },
0110                 Qt::QueuedConnection);
0111         }
0112     }
0113     QMetaObject::invokeMethod(this, &FileContentIndexer::done, Qt::QueuedConnection);
0114 }
0115 
0116 void FileContentIndexer::slotStartedIndexingFile(const QString& filePath)
0117 {
0118     m_currentFile = filePath;
0119     if (!m_registeredMonitors.isEmpty()) {
0120         Q_EMIT startedIndexingFile(filePath);
0121     }
0122 }
0123 
0124 void FileContentIndexer::slotFinishedIndexingFile(const QString& filePath, bool fileUpdated)
0125 {
0126     if (fileUpdated) {
0127         m_updatedFiles.append(filePath);
0128     }
0129 
0130     m_currentFile = QString();
0131     if (!m_registeredMonitors.isEmpty()) {
0132         Q_EMIT finishedIndexingFile(filePath);
0133     }
0134 }
0135 
0136 void FileContentIndexer::registerMonitor(const QDBusMessage& message)
0137 {
0138     if (!m_registeredMonitors.contains(message.service())) {
0139         m_registeredMonitors << message.service();
0140         m_monitorWatcher.addWatchedService(message.service());
0141     }
0142 }
0143 
0144 void FileContentIndexer::unregisterMonitor(const QDBusMessage& message)
0145 {
0146     m_registeredMonitors.removeAll(message.service());
0147     m_monitorWatcher.removeWatchedService(message.service());
0148 }
0149 
0150 void FileContentIndexer::monitorClosed(const QString& service)
0151 {
0152     m_registeredMonitors.removeAll(service);
0153     m_monitorWatcher.removeWatchedService(service);
0154 }
0155 
0156 #include "moc_filecontentindexer.cpp"