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

0001 /*
0002     This file is part of the KDE Baloo project.
0003     SPDX-FileCopyrightText: 2011 Sebastian Trueg <trueg@kde.org>
0004     SPDX-FileCopyrightText: 2013-2014 Vishesh Handa <me@vhanda.in>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "pendingfilequeue.h"
0010 
0011 #include <memory>
0012 
0013 #include <QDateTime>
0014 
0015 using namespace Baloo;
0016 
0017 PendingFileQueue::PendingFileQueue(QObject* parent)
0018     : QObject(parent)
0019 {
0020     m_cacheTimer.setInterval(10);
0021     m_cacheTimer.setSingleShot(true);
0022     connect(&m_cacheTimer, &QTimer::timeout, [this] {
0023             PendingFileQueue::processCache(QTime::currentTime());
0024     });
0025 
0026     m_trackingTime = 120 * 1000;
0027 
0028     m_clearRecentlyEmittedTimer.setInterval(m_trackingTime);
0029     m_clearRecentlyEmittedTimer.setSingleShot(true);
0030     connect(&m_clearRecentlyEmittedTimer, &QTimer::timeout, [this] {
0031             PendingFileQueue::clearRecentlyEmitted(QTime::currentTime());
0032     });
0033 
0034     m_minTimeout = 5 * 1000;
0035     m_maxTimeout = 60 * 1000;
0036     m_pendingFilesTimer.setInterval(m_minTimeout);
0037     m_pendingFilesTimer.setSingleShot(true);
0038     connect(&m_pendingFilesTimer, &QTimer::timeout, [this] {
0039             PendingFileQueue::processPendingFiles(QTime::currentTime());
0040     });
0041 }
0042 
0043 PendingFileQueue::~PendingFileQueue()
0044 {
0045 }
0046 
0047 void PendingFileQueue::enqueue(const PendingFile& file)
0048 {
0049     // If we get an event to remove /home/A, remove all events for everything under /home/A/
0050 
0051     if (file.shouldRemoveIndex() && file.path().endsWith(QLatin1Char('/'))) {
0052         const auto keepFile = [&file](const PendingFile& pending) {
0053             return !pending.path().startsWith(file.path());
0054         };
0055         const auto end = m_cache.end();
0056         // std::partition moves all matching entries to the first partition
0057         const auto droppedFilesBegin = std::partition(m_cache.begin(), end, keepFile);
0058         for (auto it = droppedFilesBegin; it != end; it++) {
0059             m_pendingFiles.remove(it->path());
0060             m_recentlyEmitted.remove(it->path());
0061         }
0062         m_cache.erase(droppedFilesBegin, end);
0063     }
0064 
0065     if (file.shouldRemoveIndex()) {
0066         m_cache.removeOne(file);
0067         m_pendingFiles.remove(file.path());
0068         Q_EMIT removeFileIndex(file.path());
0069         return;
0070     }
0071 
0072     int i = m_cache.indexOf(file);
0073     if (i == -1) {
0074         m_cache << file;
0075     } else {
0076         m_cache[i].merge(file);
0077     }
0078 
0079     m_cacheTimer.start();
0080 }
0081 
0082 void PendingFileQueue::processCache(const QTime& currentTime)
0083 {
0084     for (const PendingFile& file : std::as_const(m_cache)) {
0085         if (file.shouldIndexXAttrOnly()) {
0086             Q_EMIT indexXAttr(file.path());
0087         }
0088         else if (file.shouldIndexContents()) {
0089             if (m_pendingFiles.contains(file.path())) {
0090                 QTime time = m_pendingFiles[file.path()];
0091 
0092                 int msecondsLeft = currentTime.msecsTo(time);
0093                 msecondsLeft = qBound(m_minTimeout, msecondsLeft * 2, m_maxTimeout);
0094 
0095                 time = currentTime.addMSecs(msecondsLeft);
0096                 m_pendingFiles[file.path()] = time;
0097             }
0098             else if (m_recentlyEmitted.contains(file.path())) {
0099                 QTime time = currentTime.addMSecs(m_minTimeout);
0100                 m_pendingFiles[file.path()] = time;
0101             }
0102             else {
0103                 if (file.isNewFile()) {
0104                     Q_EMIT indexNewFile(file.path());
0105                 } else {
0106                     Q_EMIT indexModifiedFile(file.path());
0107                 }
0108                 m_recentlyEmitted.insert(file.path(), currentTime);
0109             }
0110         } else {
0111             Q_ASSERT_X(false, "FileWatch", "The PendingFile should always have some flags set");
0112         }
0113     }
0114 
0115     m_cache.clear();
0116 
0117     if (!m_pendingFiles.isEmpty() && !m_pendingFilesTimer.isActive()) {
0118         m_pendingFilesTimer.setInterval(m_minTimeout);
0119         m_pendingFilesTimer.start();
0120     }
0121 
0122     if (!m_recentlyEmitted.isEmpty() && !m_clearRecentlyEmittedTimer.isActive()) {
0123         m_clearRecentlyEmittedTimer.setInterval(m_trackingTime);
0124         m_clearRecentlyEmittedTimer.start();
0125     }
0126 }
0127 
0128 void PendingFileQueue::clearRecentlyEmitted(const QTime& time)
0129 {
0130     int nextUpdate = m_trackingTime;
0131 
0132     QMutableHashIterator<QString, QTime> it(m_recentlyEmitted);
0133     while (it.hasNext()) {
0134         it.next();
0135 
0136         int msecondsSinceEmitted = it.value().msecsTo(time);
0137         if (msecondsSinceEmitted >= m_trackingTime) {
0138             it.remove();
0139         } else {
0140             int timeLeft = m_trackingTime - msecondsSinceEmitted;
0141             nextUpdate = qMin(nextUpdate, timeLeft);
0142         }
0143     }
0144 
0145     if (!m_recentlyEmitted.isEmpty()) {
0146         m_clearRecentlyEmittedTimer.setInterval(nextUpdate);
0147         m_clearRecentlyEmittedTimer.start();
0148     }
0149 }
0150 
0151 void PendingFileQueue::processPendingFiles(const QTime& currentTime)
0152 {
0153     int nextUpdate = m_maxTimeout;
0154 
0155     QMutableHashIterator<QString, QTime> it(m_pendingFiles);
0156     while (it.hasNext()) {
0157         it.next();
0158 
0159         int mSecondsLeft = currentTime.msecsTo(it.value());
0160         if (mSecondsLeft <= 0) {
0161             Q_EMIT indexModifiedFile(it.key());
0162             m_recentlyEmitted.insert(it.key(), currentTime);
0163 
0164             it.remove();
0165         }
0166         else {
0167             nextUpdate = qMin(mSecondsLeft, nextUpdate);
0168         }
0169     }
0170 
0171     if (!m_pendingFiles.isEmpty()) {
0172         m_pendingFilesTimer.setInterval(nextUpdate);
0173         m_pendingFilesTimer.start();
0174     }
0175 
0176     if (!m_recentlyEmitted.isEmpty() && !m_clearRecentlyEmittedTimer.isActive()) {
0177         m_clearRecentlyEmittedTimer.setInterval(m_trackingTime);
0178         m_clearRecentlyEmittedTimer.start();
0179     }
0180 }
0181 
0182 void PendingFileQueue::setTrackingTime(int seconds)
0183 {
0184     m_trackingTime = seconds * 1000;
0185 }
0186 
0187 void PendingFileQueue::setMinimumTimeout(int seconds)
0188 {
0189     m_minTimeout = seconds * 1000;
0190 }
0191 
0192 void PendingFileQueue::setMaximumTimeout(int seconds)
0193 {
0194     m_maxTimeout = seconds * 1000;
0195 }
0196 
0197 #include "moc_pendingfilequeue.cpp"