Merge pull request #7797 from zzmp/fix/cache-thread-safety

Make ResourceCache::resetResourceCounters threadsafe
This commit is contained in:
Brad Hefta-Gaub 2016-05-09 11:52:57 -07:00
commit 312ea20e15
2 changed files with 55 additions and 37 deletions

View file

@ -40,15 +40,14 @@ void ResourceCacheSharedItems::appendPendingRequest(QWeakPointer<Resource> resou
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getPendingRequests() { QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getPendingRequests() {
QList<QSharedPointer<Resource>> result; QList<QSharedPointer<Resource>> result;
Lock lock(_mutex);
{ foreach(QSharedPointer<Resource> resource, _pendingRequests) {
Lock lock(_mutex); if (resource) {
foreach(QSharedPointer<Resource> resource, _pendingRequests) { result.append(resource);
if (resource) {
result.append(resource);
}
} }
} }
return result; return result;
} }
@ -59,20 +58,20 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const {
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getLoadingRequests() { QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getLoadingRequests() {
QList<QSharedPointer<Resource>> result; QList<QSharedPointer<Resource>> result;
Lock lock(_mutex);
{ foreach(QSharedPointer<Resource> resource, _loadingRequests) {
Lock lock(_mutex); if (resource) {
foreach(QSharedPointer<Resource> resource, _loadingRequests) { result.append(resource);
if (resource) {
result.append(resource);
}
} }
} }
return result; return result;
} }
void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) { void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
Lock lock(_mutex); Lock lock(_mutex);
// resource can only be removed if it still has a ref-count, as // resource can only be removed if it still has a ref-count, as
// QWeakPointer has no operator== implementation for two weak ptrs, so // QWeakPointer has no operator== implementation for two weak ptrs, so
// manually loop in case resource has been freed. // manually loop in case resource has been freed.
@ -88,11 +87,11 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
} }
QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() { QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
Lock lock(_mutex);
// look for the highest priority pending request // look for the highest priority pending request
int highestIndex = -1; int highestIndex = -1;
float highestPriority = -FLT_MAX; float highestPriority = -FLT_MAX;
QSharedPointer<Resource> highestResource; QSharedPointer<Resource> highestResource;
Lock lock(_mutex);
for (int i = 0; i < _pendingRequests.size();) { for (int i = 0; i < _pendingRequests.size();) {
// Clear any freed resources // Clear any freed resources
@ -262,12 +261,14 @@ void ResourceCache::refreshAll() {
clearUnusedResource(); clearUnusedResource();
resetResourceCounters(); resetResourceCounters();
_resourcesLock.lockForRead(); QHash<QUrl, QWeakPointer<Resource>> resources;
auto resourcesCopy = _resources; {
_resourcesLock.unlock(); QReadLocker locker(&_resourcesLock);
resources = _resources;
}
// Refresh all remaining resources in use // Refresh all remaining resources in use
foreach (QSharedPointer<Resource> resource, resourcesCopy) { foreach (QSharedPointer<Resource> resource, resources) {
if (resource) { if (resource) {
resource->refresh(); resource->refresh();
} }
@ -317,17 +318,17 @@ void ResourceCache::setRequestLimit(int limit) {
void ResourceCache::getResourceAsynchronously(const QUrl& url) { void ResourceCache::getResourceAsynchronously(const QUrl& url) {
qCDebug(networking) << "ResourceCache::getResourceAsynchronously" << url.toString(); qCDebug(networking) << "ResourceCache::getResourceAsynchronously" << url.toString();
_resourcesToBeGottenLock.lockForWrite(); QWriteLocker locker(&_resourcesToBeGottenLock);
_resourcesToBeGotten.enqueue(QUrl(url)); _resourcesToBeGotten.enqueue(QUrl(url));
_resourcesToBeGottenLock.unlock();
} }
void ResourceCache::checkAsynchronousGets() { void ResourceCache::checkAsynchronousGets() {
assert(QThread::currentThread() == thread()); assert(QThread::currentThread() == thread());
QWriteLocker locker(&_resourcesToBeGottenLock);
if (!_resourcesToBeGotten.isEmpty()) { if (!_resourcesToBeGotten.isEmpty()) {
_resourcesToBeGottenLock.lockForWrite();
QUrl url = _resourcesToBeGotten.dequeue(); QUrl url = _resourcesToBeGotten.dequeue();
_resourcesToBeGottenLock.unlock();
locker.unlock();
getResource(url); getResource(url);
} }
} }
@ -399,8 +400,10 @@ void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resourc
if (_unusedResources.contains(resource->getLRUKey())) { if (_unusedResources.contains(resource->getLRUKey())) {
_unusedResources.remove(resource->getLRUKey()); _unusedResources.remove(resource->getLRUKey());
_unusedResourcesSize -= resource->getBytes(); _unusedResourcesSize -= resource->getBytes();
locker.unlock();
resetResourceCounters();
} }
resetResourceCounters();
} }
void ResourceCache::reserveUnusedResource(qint64 resourceSize) { void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
@ -413,7 +416,9 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
it.value()->setCache(nullptr); it.value()->setCache(nullptr);
auto size = it.value()->getBytes(); auto size = it.value()->getBytes();
locker.unlock();
removeResource(it.value()->getURL(), size); removeResource(it.value()->getURL(), size);
locker.relock();
_unusedResourcesSize -= size; _unusedResourcesSize -= size;
_unusedResources.erase(it); _unusedResources.erase(it);
@ -433,8 +438,16 @@ void ResourceCache::clearUnusedResource() {
} }
void ResourceCache::resetResourceCounters() { void ResourceCache::resetResourceCounters() {
_numTotalResources = _resources.size(); {
_numUnusedResources = _unusedResources.size(); QReadLocker locker(&_resourcesLock);
_numTotalResources = _resources.size();
}
{
QReadLocker locker(&_unusedResourcesLock);
_numUnusedResources = _unusedResources.size();
}
emit dirty(); emit dirty();
} }

View file

@ -64,6 +64,7 @@ class ResourceCacheSharedItems : public Dependency {
using Mutex = std::mutex; using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
public: public:
void appendPendingRequest(QWeakPointer<Resource> newRequest); void appendPendingRequest(QWeakPointer<Resource> newRequest);
void appendActiveRequest(QWeakPointer<Resource> newRequest); void appendActiveRequest(QWeakPointer<Resource> newRequest);
@ -224,26 +225,30 @@ private:
void resetResourceCounters(); void resetResourceCounters();
void removeResource(const QUrl& url, qint64 size = 0); void removeResource(const QUrl& url, qint64 size = 0);
QReadWriteLock _resourcesLock { QReadWriteLock::Recursive }; void getResourceAsynchronously(const QUrl& url);
QHash<QUrl, QWeakPointer<Resource>> _resources;
int _lastLRUKey = 0;
static int _requestLimit; static int _requestLimit;
static int _requestsActive; static int _requestsActive;
void getResourceAsynchronously(const QUrl& url); // Resources
QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive }; QHash<QUrl, QWeakPointer<Resource>> _resources;
QQueue<QUrl> _resourcesToBeGotten; QReadWriteLock _resourcesLock { QReadWriteLock::Recursive };
int _lastLRUKey = 0;
std::atomic<size_t> _numTotalResources { 0 };
std::atomic<size_t> _numUnusedResources { 0 };
std::atomic<size_t> _numTotalResources { 0 };
std::atomic<qint64> _totalResourcesSize { 0 }; std::atomic<qint64> _totalResourcesSize { 0 };
// Cached resources
QMap<int, QSharedPointer<Resource>> _unusedResources;
QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive };
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
std::atomic<size_t> _numUnusedResources { 0 };
std::atomic<qint64> _unusedResourcesSize { 0 }; std::atomic<qint64> _unusedResourcesSize { 0 };
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; // Pending resources
QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive }; QQueue<QUrl> _resourcesToBeGotten;
QMap<int, QSharedPointer<Resource>> _unusedResources; QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive };
}; };
/// Base class for resources. /// Base class for resources.