summaryrefslogtreecommitdiffstats
path: root/src/network/access/qnetworkaccesscache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qnetworkaccesscache.cpp')
-rw-r--r--src/network/access/qnetworkaccesscache.cpp242
1 files changed, 99 insertions, 143 deletions
diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp
index 4d65761a0b..2bc0e8fb70 100644
--- a/src/network/access/qnetworkaccesscache.cpp
+++ b/src/network/access/qnetworkaccesscache.cpp
@@ -1,51 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccesscache_p.h"
#include "QtCore/qpointer.h"
-#include "QtCore/qdatetime.h"
+#include "QtCore/qdeadlinetimer.h"
#include "qnetworkaccessmanager_p.h"
#include "qnetworkreply_p.h"
#include "qnetworkrequest.h"
#include <vector>
+//#define DEBUG_ACCESSCACHE
+
QT_BEGIN_NAMESPACE
enum ExpiryTimeEnum {
@@ -63,18 +29,14 @@ namespace {
// idea copied from qcache.h
struct QNetworkAccessCache::Node
{
- QDateTime timestamp;
- std::vector<Receiver> receiverQueue;
+ QDeadlineTimer timer;
QByteArray key;
- Node *older, *newer;
- CacheableObject *object;
-
- int useCount;
+ Node *previous = nullptr; // "previous" nodes expire "previous"ly (before us)
+ Node *next = nullptr; // "next" nodes expire "next" (after us)
+ CacheableObject *object = nullptr;
- Node()
- : older(nullptr), newer(nullptr), object(nullptr), useCount(0)
- { }
+ int useCount = 0;
};
QNetworkAccessCache::CacheableObject::CacheableObject()
@@ -102,11 +64,6 @@ void QNetworkAccessCache::CacheableObject::setShareable(bool enable)
shareable = enable;
}
-QNetworkAccessCache::QNetworkAccessCache()
- : oldest(nullptr), newest(nullptr)
-{
-}
-
QNetworkAccessCache::~QNetworkAccessCache()
{
clear();
@@ -131,7 +88,7 @@ void QNetworkAccessCache::clear()
timer.stop();
- oldest = newest = nullptr;
+ firstExpiringNode = lastExpiringNode = nullptr;
}
/*!
@@ -144,27 +101,60 @@ void QNetworkAccessCache::linkEntry(const QByteArray &key)
if (!node)
return;
- Q_ASSERT(node != oldest && node != newest);
- Q_ASSERT(node->older == nullptr && node->newer == nullptr);
+ Q_ASSERT(node != firstExpiringNode && node != lastExpiringNode);
+ Q_ASSERT(node->previous == nullptr && node->next == nullptr);
Q_ASSERT(node->useCount == 0);
- if (newest) {
- Q_ASSERT(newest->newer == nullptr);
- newest->newer = node;
- node->older = newest;
- }
- if (!oldest) {
- // there are no entries, so this is the oldest one too
- oldest = node;
+
+ node->timer.setPreciseRemainingTime(node->object->expiryTimeoutSeconds);
+#ifdef DEBUG_ACCESSCACHE
+ qDebug() << "QNetworkAccessCache case trying to insert=" << QString::fromUtf8(key)
+ << node->timer.remainingTime() << "milliseconds";
+ Node *current = lastExpiringNode;
+ while (current) {
+ qDebug() << "QNetworkAccessCache item=" << QString::fromUtf8(current->key)
+ << current->timer.remainingTime() << "milliseconds"
+ << (current == lastExpiringNode ? "[last to expire]" : "")
+ << (current == firstExpiringNode ? "[first to expire]" : "");
+ current = current->previous;
}
+#endif
- node->timestamp = QDateTime::currentDateTimeUtc().addSecs(ExpiryTime);
- newest = node;
+ if (lastExpiringNode) {
+ Q_ASSERT(lastExpiringNode->next == nullptr);
+ if (lastExpiringNode->timer < node->timer) {
+ // Insert as new last-to-expire node.
+ node->previous = lastExpiringNode;
+ lastExpiringNode->next = node;
+ lastExpiringNode = node;
+ } else {
+ // Insert in a sorted way, as different nodes might have had different expiryTimeoutSeconds set.
+ Node *current = lastExpiringNode;
+ while (current->previous != nullptr && current->previous->timer >= node->timer)
+ current = current->previous;
+ node->previous = current->previous;
+ if (node->previous)
+ node->previous->next = node;
+ node->next = current;
+ current->previous = node;
+ if (node->previous == nullptr)
+ firstExpiringNode = node;
+ }
+ } else {
+ // no current last-to-expire node
+ lastExpiringNode = node;
+ }
+ if (!firstExpiringNode) {
+ // there are no entries, so this is the next-to-expire too
+ firstExpiringNode = node;
+ }
+ Q_ASSERT(firstExpiringNode->previous == nullptr);
+ Q_ASSERT(lastExpiringNode->next == nullptr);
}
/*!
Removes the entry pointed by \a key from the linked list.
- Returns \c true if the entry removed was the oldest one.
+ Returns \c true if the entry removed was the next to expire.
*/
bool QNetworkAccessCache::unlinkEntry(const QByteArray &key)
{
@@ -172,38 +162,39 @@ bool QNetworkAccessCache::unlinkEntry(const QByteArray &key)
if (!node)
return false;
- bool wasOldest = false;
- if (node == oldest) {
- oldest = node->newer;
- wasOldest = true;
+ bool wasFirst = false;
+ if (node == firstExpiringNode) {
+ firstExpiringNode = node->next;
+ wasFirst = true;
}
- if (node == newest)
- newest = node->older;
- if (node->older)
- node->older->newer = node->newer;
- if (node->newer)
- node->newer->older = node->older;
-
- node->newer = node->older = nullptr;
- return wasOldest;
+ if (node == lastExpiringNode)
+ lastExpiringNode = node->previous;
+ if (node->previous)
+ node->previous->next = node->next;
+ if (node->next)
+ node->next->previous = node->previous;
+
+ node->next = node->previous = nullptr;
+ return wasFirst;
}
void QNetworkAccessCache::updateTimer()
{
timer.stop();
- if (!oldest)
+ if (!firstExpiringNode)
return;
- int interval = QDateTime::currentDateTimeUtc().secsTo(oldest->timestamp);
+ qint64 interval = firstExpiringNode->timer.remainingTime();
if (interval <= 0) {
interval = 0;
- } else {
- // round up the interval
- interval = (interval + 15) & ~16;
}
- timer.start(interval * 1000, this);
+ // Plus 10 msec so we don't spam timer events if date comparisons are too fuzzy.
+ // This code used to do (broken) rounding, but for ConnectionCacheExpiryTimeoutSecondsAttribute
+ // to work we cannot do this.
+ // See discussion in https://codereview.qt-project.org/c/qt/qtbase/+/337464
+ timer.start(interval + 10, this);
}
bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char *member)
@@ -220,28 +211,24 @@ bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char
void QNetworkAccessCache::timerEvent(QTimerEvent *)
{
- // expire old items
- const QDateTime now = QDateTime::currentDateTimeUtc();
-
- while (oldest && oldest->timestamp < now) {
- Node *next = oldest->newer;
- oldest->object->dispose();
-
- hash.remove(oldest->key); // oldest gets deleted
- delete oldest;
- oldest = next;
+ while (firstExpiringNode && firstExpiringNode->timer.hasExpired()) {
+ Node *next = firstExpiringNode->next;
+ firstExpiringNode->object->dispose();
+ hash.remove(firstExpiringNode->key); // `firstExpiringNode` gets deleted
+ delete firstExpiringNode;
+ firstExpiringNode = next;
}
// fixup the list
- if (oldest)
- oldest->older = nullptr;
+ if (firstExpiringNode)
+ firstExpiringNode->previous = nullptr;
else
- newest = nullptr;
+ lastExpiringNode = nullptr;
updateTimer();
}
-void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry)
+void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry, qint64 connectionCacheExpiryTimeoutSeconds)
{
Q_ASSERT(!key.isEmpty());
@@ -260,8 +247,15 @@ void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry
node->object->dispose();
node->object = entry;
node->object->key = key;
+ if (connectionCacheExpiryTimeoutSeconds > -1) {
+ node->object->expiryTimeoutSeconds = connectionCacheExpiryTimeoutSeconds; // via ConnectionCacheExpiryTimeoutSecondsAttribute
+ } else {
+ node->object->expiryTimeoutSeconds = ExpiryTime;
+ }
node->key = key;
node->useCount = 1;
+
+ // It gets only put into the expiry list in linkEntry (from releaseEntry), when it is not used anymore.
}
bool QNetworkAccessCache::hasEntry(const QByteArray &key) const
@@ -269,30 +263,6 @@ bool QNetworkAccessCache::hasEntry(const QByteArray &key) const
return hash.contains(key);
}
-bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, const char *member)
-{
- Node *node = hash.value(key);
- if (!node)
- return false;
-
- if (node->useCount > 0 && !node->object->shareable) {
- // object is not shareable and is in use
- // queue for later use
- Q_ASSERT(node->older == nullptr && node->newer == nullptr);
- node->receiverQueue.push_back({target, member});
-
- // request queued
- return true;
- } else {
- // node not in use or is shareable
- if (unlinkEntry(key))
- updateTimer();
-
- ++node->useCount;
- return emitEntryReady(node, target, member);
- }
-}
-
QNetworkAccessCache::CacheableObject *QNetworkAccessCache::requestEntryNow(const QByteArray &key)
{
Node *node = hash.value(key);
@@ -310,10 +280,10 @@ QNetworkAccessCache::CacheableObject *QNetworkAccessCache::requestEntryNow(const
}
// entry not in use, let the caller have it
- bool wasOldest = unlinkEntry(key);
+ bool wasNext = unlinkEntry(key);
++node->useCount;
- if (wasOldest)
+ if (wasNext)
updateTimer();
return node->object;
}
@@ -328,28 +298,12 @@ void QNetworkAccessCache::releaseEntry(const QByteArray &key)
Q_ASSERT(node->useCount > 0);
- // are there other objects waiting?
- const auto objectStillExists = [](const Receiver &r) { return !r.object.isNull(); };
-
- auto &queue = node->receiverQueue;
- auto qit = std::find_if(queue.begin(), queue.end(), objectStillExists);
-
- const Receiver receiver = qit == queue.end() ? Receiver{} : std::move(*qit++) ;
-
- queue.erase(queue.begin(), qit);
-
- if (receiver.object) {
- // queue another activation
- emitEntryReady(node, receiver.object, receiver.member);
- return;
- }
-
if (!--node->useCount) {
// no objects waiting; add it back to the expiry list
if (node->object->expires)
linkEntry(key);
- if (oldest == node)
+ if (firstExpiringNode == node)
updateTimer();
}
}
@@ -374,3 +328,5 @@ void QNetworkAccessCache::removeEntry(const QByteArray &key)
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccesscache_p.cpp"