Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qnetworkaccesscache.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "QtCore/qpointer.h"
6#include "QtCore/qdeadlinetimer.h"
8#include "qnetworkreply_p.h"
9#include "qnetworkrequest.h"
10
11#include <vector>
12
13//#define DEBUG_ACCESSCACHE
14
16
18 ExpiryTime = 120
19};
20
21namespace {
22 struct Receiver
23 {
24 QPointer<QObject> object;
25 const char *member;
26 };
27}
28
29// idea copied from qcache.h
31{
34
35 Node *previous = nullptr; // "previous" nodes expire "previous"ly (before us)
36 Node *next = nullptr; // "next" nodes expire "next" (after us)
37 CacheableObject *object = nullptr;
38
39 int useCount = 0;
40};
41
43{
44 // leave the members uninitialized
45 // they must be initialized by the derived class's constructor
46}
47
49{
50#if 0 //def QT_DEBUG
51 if (!key.isEmpty() && Ptr()->hasEntry(key))
52 qWarning() << "QNetworkAccessCache: object" << (void*)this << "key" << key
53 << "destroyed without being removed from cache first!";
54#endif
55}
56
61
66
71
73{
74 NodeHash hashCopy = hash;
75 hash.clear();
76
77 // remove all entries
78 NodeHash::Iterator it = hashCopy.begin();
79 NodeHash::Iterator end = hashCopy.end();
80 for ( ; it != end; ++it) {
81 (*it)->object->key.clear();
82 (*it)->object->dispose();
83 delete (*it);
84 }
85
86 // now delete:
87 hashCopy.clear();
88
89 timer.stop();
90
91 firstExpiringNode = lastExpiringNode = nullptr;
92}
93
98void QNetworkAccessCache::linkEntry(const QByteArray &key)
99{
100 Node * const node = hash.value(key);
101 if (!node)
102 return;
103
104 Q_ASSERT(node != firstExpiringNode && node != lastExpiringNode);
105 Q_ASSERT(node->previous == nullptr && node->next == nullptr);
106 Q_ASSERT(node->useCount == 0);
107
108
109 node->timer.setPreciseRemainingTime(node->object->expiryTimeoutSeconds);
110#ifdef DEBUG_ACCESSCACHE
111 qDebug() << "QNetworkAccessCache case trying to insert=" << QString::fromUtf8(key)
112 << node->timer.remainingTime() << "milliseconds";
113 Node *current = lastExpiringNode;
114 while (current) {
115 qDebug() << "QNetworkAccessCache item=" << QString::fromUtf8(current->key)
116 << current->timer.remainingTime() << "milliseconds"
117 << (current == lastExpiringNode ? "[last to expire]" : "")
118 << (current == firstExpiringNode ? "[first to expire]" : "");
119 current = current->previous;
120 }
121#endif
122
123 if (lastExpiringNode) {
124 Q_ASSERT(lastExpiringNode->next == nullptr);
125 if (lastExpiringNode->timer < node->timer) {
126 // Insert as new last-to-expire node.
127 node->previous = lastExpiringNode;
128 lastExpiringNode->next = node;
129 lastExpiringNode = node;
130 } else {
131 // Insert in a sorted way, as different nodes might have had different expiryTimeoutSeconds set.
132 Node *current = lastExpiringNode;
133 while (current->previous != nullptr && current->previous->timer >= node->timer)
134 current = current->previous;
135 node->previous = current->previous;
136 if (node->previous)
137 node->previous->next = node;
138 node->next = current;
139 current->previous = node;
140 if (node->previous == nullptr)
141 firstExpiringNode = node;
142 }
143 } else {
144 // no current last-to-expire node
145 lastExpiringNode = node;
146 }
147 if (!firstExpiringNode) {
148 // there are no entries, so this is the next-to-expire too
149 firstExpiringNode = node;
150 }
151 Q_ASSERT(firstExpiringNode->previous == nullptr);
152 Q_ASSERT(lastExpiringNode->next == nullptr);
153}
154
159bool QNetworkAccessCache::unlinkEntry(const QByteArray &key)
160{
161 Node * const node = hash.value(key);
162 if (!node)
163 return false;
164
165 bool wasFirst = false;
166 if (node == firstExpiringNode) {
167 firstExpiringNode = node->next;
168 wasFirst = true;
169 }
170 if (node == lastExpiringNode)
171 lastExpiringNode = node->previous;
172 if (node->previous)
173 node->previous->next = node->next;
174 if (node->next)
175 node->next->previous = node->previous;
176
177 node->next = node->previous = nullptr;
178 return wasFirst;
179}
180
181void QNetworkAccessCache::updateTimer()
182{
183 timer.stop();
184
185 if (!firstExpiringNode)
186 return;
187
188 qint64 interval = firstExpiringNode->timer.remainingTime();
189 if (interval <= 0) {
190 interval = 0;
191 }
192
193 // Plus 10 msec so we don't spam timer events if date comparisons are too fuzzy.
194 // This code used to do (broken) rounding, but for ConnectionCacheExpiryTimeoutSecondsAttribute
195 // to work we cannot do this.
196 // See discussion in https://codereview.qt-project.org/c/qt/qtbase/+/337464
197 timer.start(interval + 10, this);
198}
199
200bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char *member)
201{
204 return false;
205
206 emit entryReady(node->object);
208
209 return true;
210}
211
213{
214 while (firstExpiringNode && firstExpiringNode->timer.hasExpired()) {
215 Node *next = firstExpiringNode->next;
216 firstExpiringNode->object->dispose();
217 hash.remove(firstExpiringNode->key); // `firstExpiringNode` gets deleted
218 delete firstExpiringNode;
219 firstExpiringNode = next;
220 }
221
222 // fixup the list
223 if (firstExpiringNode)
224 firstExpiringNode->previous = nullptr;
225 else
226 lastExpiringNode = nullptr;
227
228 updateTimer();
229}
230
231void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry, qint64 connectionCacheExpiryTimeoutSeconds)
232{
233 Q_ASSERT(!key.isEmpty());
234
235 if (unlinkEntry(key))
236 updateTimer();
237
238 Node *node = hash.value(key);
239 if (!node) {
240 node = new Node;
241 hash.insert(key, node);
242 }
243
244 if (node->useCount)
245 qWarning("QNetworkAccessCache::addEntry: overriding active cache entry '%s'", key.constData());
246 if (node->object)
247 node->object->dispose();
248 node->object = entry;
249 node->object->key = key;
250 if (connectionCacheExpiryTimeoutSeconds > -1) {
251 node->object->expiryTimeoutSeconds = connectionCacheExpiryTimeoutSeconds; // via ConnectionCacheExpiryTimeoutSecondsAttribute
252 } else {
253 node->object->expiryTimeoutSeconds = ExpiryTime;
254 }
255 node->key = key;
256 node->useCount = 1;
257
258 // It gets only put into the expiry list in linkEntry (from releaseEntry), when it is not used anymore.
259}
260
262{
263 return hash.contains(key);
264}
265
267{
268 Node *node = hash.value(key);
269 if (!node)
270 return nullptr;
271
272 if (node->useCount > 0) {
273 if (node->object->shareable) {
274 ++node->useCount;
275 return node->object;
276 }
277
278 // object in use and not shareable
279 return nullptr;
280 }
281
282 // entry not in use, let the caller have it
283 bool wasNext = unlinkEntry(key);
284 ++node->useCount;
285
286 if (wasNext)
287 updateTimer();
288 return node->object;
289}
290
292{
293 Node *node = hash.value(key);
294 if (!node) {
295 qWarning("QNetworkAccessCache::releaseEntry: trying to release key '%s' that is not in cache", key.constData());
296 return;
297 }
298
299 Q_ASSERT(node->useCount > 0);
300
301 if (!--node->useCount) {
302 // no objects waiting; add it back to the expiry list
303 if (node->object->expires)
304 linkEntry(key);
305
306 if (firstExpiringNode == node)
307 updateTimer();
308 }
309}
310
312{
313 Node *node = hash.value(key);
314 if (!node) {
315 qWarning("QNetworkAccessCache::removeEntry: trying to remove key '%s' that is not in cache", key.constData());
316 return;
317 }
318
319 if (unlinkEntry(key))
320 updateTimer();
321 if (node->useCount > 1)
322 qWarning("QNetworkAccessCache::removeEntry: removing active cache entry '%s'",
323 key.constData());
324
325 node->object->key.clear();
326 hash.remove(node->key);
327 delete node;
328}
329
331
332#include "moc_qnetworkaccesscache_p.cpp"
Definition lalr.h:136
void start(int msec, QObject *obj)
\obsolete Use chrono overload instead.
void stop()
Stops the timer.
\inmodule QtCore
Definition qbytearray.h:57
void clear()
Clears the contents of the byte array and makes it null.
\inmodule QtCore
bool hasExpired() const noexcept
Returns true if this QDeadlineTimer object has expired, false if there remains time left.
qint64 remainingTime() const noexcept
Returns the remaining time in this QDeadlineTimer object in milliseconds.
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
iterator Iterator
Qt-style synonym for QHash::iterator.
Definition qhash.h:1288
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
T value(const Key &key) const noexcept
Definition qhash.h:1054
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
bool hasEntry(const QByteArray &key) const
void removeEntry(const QByteArray &key)
CacheableObject * requestEntryNow(const QByteArray &key)
void addEntry(const QByteArray &key, CacheableObject *entry, qint64 connectionCacheExpiryTimeoutSeconds=-1)
void entryReady(QNetworkAccessCache::CacheableObject *)
void releaseEntry(const QByteArray &key)
void timerEvent(QTimerEvent *) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
iterator begin()
Definition qset.h:136
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
\inmodule QtCore
Definition qcoreevent.h:366
QSet< QString >::iterator it
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
@ QueuedConnection
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLuint64 key
GLuint GLuint end
GLuint object
[3]
GLenum target
GLboolean enable
GLuint entry
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
long long qint64
Definition qtypes.h:60
myObject disconnect()
[26]