1/*
2 This file is part of the KDE Baloo project.
3 SPDX-FileCopyrightText: 2011 Sebastian Trueg <trueg@kde.org>
4 SPDX-FileCopyrightText: 2013-2014 Vishesh Handa <me@vhanda.in>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7*/
8
9#include "pendingfilequeue.h"
10
11#include <memory>
12
13#include <QDateTime>
14
15using namespace Baloo;
16
17PendingFileQueue::PendingFileQueue(QObject* parent)
18 : QObject(parent)
19{
20 m_cacheTimer.setInterval(10);
21 m_cacheTimer.setSingleShot(true);
22 connect(sender: &m_cacheTimer, signal: &QTimer::timeout, slot: [this] {
23 PendingFileQueue::processCache(currentTime: QTime::currentTime());
24 });
25
26 m_trackingTime = 120 * 1000;
27
28 m_clearRecentlyEmittedTimer.setInterval(m_trackingTime);
29 m_clearRecentlyEmittedTimer.setSingleShot(true);
30 connect(sender: &m_clearRecentlyEmittedTimer, signal: &QTimer::timeout, slot: [this] {
31 PendingFileQueue::clearRecentlyEmitted(currentTime: QTime::currentTime());
32 });
33
34 m_minTimeout = 5 * 1000;
35 m_maxTimeout = 60 * 1000;
36 m_pendingFilesTimer.setInterval(m_minTimeout);
37 m_pendingFilesTimer.setSingleShot(true);
38 connect(sender: &m_pendingFilesTimer, signal: &QTimer::timeout, slot: [this] {
39 PendingFileQueue::processPendingFiles(currentTime: QTime::currentTime());
40 });
41}
42
43PendingFileQueue::~PendingFileQueue()
44{
45}
46
47void PendingFileQueue::enqueue(const PendingFile& file)
48{
49 // If we get an event to remove /home/A, remove all events for everything under /home/A/
50
51 if (file.shouldRemoveIndex() && file.path().endsWith(c: QLatin1Char('/'))) {
52 const auto keepFile = [&file](const PendingFile& pending) {
53 return !pending.path().startsWith(s: file.path());
54 };
55 const auto end = m_cache.end();
56 // std::partition moves all matching entries to the first partition
57 const auto droppedFilesBegin = std::partition(first: m_cache.begin(), last: end, pred: keepFile);
58 for (auto it = droppedFilesBegin; it != end; it++) {
59 m_pendingFiles.remove(key: it->path());
60 m_recentlyEmitted.remove(key: it->path());
61 }
62 m_cache.erase(abegin: droppedFilesBegin, aend: end);
63 }
64
65 if (file.shouldRemoveIndex()) {
66 m_cache.removeOne(t: file);
67 m_pendingFiles.remove(key: file.path());
68 Q_EMIT removeFileIndex(fileUrl: file.path());
69 return;
70 }
71
72 int i = m_cache.indexOf(t: file);
73 if (i == -1) {
74 m_cache << file;
75 } else {
76 m_cache[i].merge(file);
77 }
78
79 m_cacheTimer.start();
80}
81
82void PendingFileQueue::processCache(const QTime& currentTime)
83{
84 for (const PendingFile& file : std::as_const(t&: m_cache)) {
85 if (file.shouldIndexXAttrOnly()) {
86 Q_EMIT indexXAttr(fileUrl: file.path());
87 }
88 else if (file.shouldIndexContents()) {
89 if (m_pendingFiles.contains(key: file.path())) {
90 QTime time = m_pendingFiles[file.path()];
91
92 int msecondsLeft = currentTime.msecsTo(t: time);
93 msecondsLeft = qBound(min: m_minTimeout, val: msecondsLeft * 2, max: m_maxTimeout);
94
95 time = currentTime.addMSecs(ms: msecondsLeft);
96 m_pendingFiles[file.path()] = time;
97 }
98 else if (m_recentlyEmitted.contains(key: file.path())) {
99 QTime time = currentTime.addMSecs(ms: m_minTimeout);
100 m_pendingFiles[file.path()] = time;
101 }
102 else {
103 if (file.isNewFile()) {
104 Q_EMIT indexNewFile(fileUrl: file.path());
105 } else {
106 Q_EMIT indexModifiedFile(fileUrl: file.path());
107 }
108 m_recentlyEmitted.insert(key: file.path(), value: currentTime);
109 }
110 } else {
111 Q_ASSERT_X(false, "FileWatch", "The PendingFile should always have some flags set");
112 }
113 }
114
115 m_cache.clear();
116
117 if (!m_pendingFiles.isEmpty() && !m_pendingFilesTimer.isActive()) {
118 m_pendingFilesTimer.setInterval(m_minTimeout);
119 m_pendingFilesTimer.start();
120 }
121
122 if (!m_recentlyEmitted.isEmpty() && !m_clearRecentlyEmittedTimer.isActive()) {
123 m_clearRecentlyEmittedTimer.setInterval(m_trackingTime);
124 m_clearRecentlyEmittedTimer.start();
125 }
126}
127
128void PendingFileQueue::clearRecentlyEmitted(const QTime& time)
129{
130 int nextUpdate = m_trackingTime;
131
132 QMutableHashIterator<QString, QTime> it(m_recentlyEmitted);
133 while (it.hasNext()) {
134 it.next();
135
136 int msecondsSinceEmitted = it.value().msecsTo(t: time);
137 if (msecondsSinceEmitted >= m_trackingTime) {
138 it.remove();
139 } else {
140 int timeLeft = m_trackingTime - msecondsSinceEmitted;
141 nextUpdate = qMin(a: nextUpdate, b: timeLeft);
142 }
143 }
144
145 if (!m_recentlyEmitted.isEmpty()) {
146 m_clearRecentlyEmittedTimer.setInterval(nextUpdate);
147 m_clearRecentlyEmittedTimer.start();
148 }
149}
150
151void PendingFileQueue::processPendingFiles(const QTime& currentTime)
152{
153 int nextUpdate = m_maxTimeout;
154
155 QMutableHashIterator<QString, QTime> it(m_pendingFiles);
156 while (it.hasNext()) {
157 it.next();
158
159 int mSecondsLeft = currentTime.msecsTo(t: it.value());
160 if (mSecondsLeft <= 0) {
161 Q_EMIT indexModifiedFile(fileUrl: it.key());
162 m_recentlyEmitted.insert(key: it.key(), value: currentTime);
163
164 it.remove();
165 }
166 else {
167 nextUpdate = qMin(a: mSecondsLeft, b: nextUpdate);
168 }
169 }
170
171 if (!m_pendingFiles.isEmpty()) {
172 m_pendingFilesTimer.setInterval(nextUpdate);
173 m_pendingFilesTimer.start();
174 }
175
176 if (!m_recentlyEmitted.isEmpty() && !m_clearRecentlyEmittedTimer.isActive()) {
177 m_clearRecentlyEmittedTimer.setInterval(m_trackingTime);
178 m_clearRecentlyEmittedTimer.start();
179 }
180}
181
182void PendingFileQueue::setTrackingTime(int seconds)
183{
184 m_trackingTime = seconds * 1000;
185}
186
187void PendingFileQueue::setMinimumTimeout(int seconds)
188{
189 m_minTimeout = seconds * 1000;
190}
191
192void PendingFileQueue::setMaximumTimeout(int seconds)
193{
194 m_maxTimeout = seconds * 1000;
195}
196
197#include "moc_pendingfilequeue.cpp"
198

source code of baloo/src/file/pendingfilequeue.cpp