1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-03 07:09:40 +00:00

Merge GenericObjectCache update and remove functions

They are always called together. Single iteration over the items is more
efficient along with locking the mutex only once.
This commit is contained in:
elsid 2023-12-15 23:14:44 +01:00
parent 71e33cf8b2
commit 56401a90a1
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
3 changed files with 32 additions and 74 deletions

View file

@ -63,9 +63,7 @@ namespace Resource
const double referenceTime = 1000;
const double expiryDelay = 1;
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
}
@ -92,16 +90,13 @@ namespace Resource
const int key = 42;
cache->addEntryToObjectCache(key, nullptr, referenceTime + expiryDelay);
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay);
cache->removeExpiredObjectsInCache(referenceTime);
cache->update(referenceTime + expiryDelay, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + 2 * expiryDelay);
cache->removeExpiredObjectsInCache(referenceTime + expiryDelay);
cache->update(referenceTime + 2 * expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
@ -117,12 +112,10 @@ namespace Resource
cache->addEntryToObjectCache(key, value);
value = nullptr;
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay);
cache->removeExpiredObjectsInCache(referenceTime);
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
@ -137,12 +130,10 @@ namespace Resource
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay);
cache->removeExpiredObjectsInCache(referenceTime);
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(value));
}
@ -158,12 +149,10 @@ namespace Resource
cache->addEntryToObjectCache(key, value);
value = nullptr;
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime + expiryDelay, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay / 2);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay / 2);
cache->update(referenceTime + expiryDelay / 2, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
}
@ -177,12 +166,10 @@ namespace Resource
const int key = 42;
cache->addEntryToObjectCache(key, nullptr);
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime + expiryDelay, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay / 2);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay / 2);
cache->update(referenceTime + expiryDelay / 2, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
}
@ -196,16 +183,13 @@ namespace Resource
const int key = 42;
cache->addEntryToObjectCache(key, nullptr);
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay / 2);
cache->removeExpiredObjectsInCache(referenceTime - expiryDelay / 2);
cache->update(referenceTime + expiryDelay / 2, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime + expiryDelay);
cache->removeExpiredObjectsInCache(referenceTime);
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}

View file

@ -24,6 +24,7 @@
#include <osg/Referenced>
#include <osg/ref_ptr>
#include <algorithm>
#include <map>
#include <mutex>
#include <optional>
@ -48,48 +49,25 @@ namespace Resource
{
}
/** For each object in the cache which has an reference count greater than 1
* (and therefore referenced by elsewhere in the application) set the time stamp
* for that object in the cache to specified time.
* This would typically be called once per frame by applications which are doing database paging,
* and need to prune objects that are no longer required.
* The time used should be taken from the FrameStamp::getReferenceTime().*/
void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime)
{
// look for objects with external references and update their time stamp.
std::lock_guard<std::mutex> lock(_objectCacheMutex);
for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
{
// If ref count is greater than 1, the object has an external reference.
// If the timestamp is yet to be initialized, it needs to be updated too.
if ((itr->second.mValue != nullptr && itr->second.mValue->referenceCount() > 1)
|| itr->second.mLastUsage == 0.0)
itr->second.mLastUsage = referenceTime;
}
}
/** Removed object in the cache which have a time stamp at or before the specified expiry time.
* This would typically be called once per frame by applications which are doing database paging,
* and need to prune objects that are no longer required, and called after the a called
* after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/
void removeExpiredObjectsInCache(double expiryTime)
// Update last usage timestamp using referenceTime for each cache time if they are not nullptr and referenced
// from somewhere else. Remove items with last usage > expiryTime. Note: last usage might be updated from other
// places so nullptr or not references elsewhere items are not always removed.
void update(double referenceTime, double expiryDelay)
{
std::vector<osg::ref_ptr<osg::Object>> objectsToRemove;
{
const double expiryTime = referenceTime - expiryDelay;
std::lock_guard<std::mutex> lock(_objectCacheMutex);
// Remove expired entries from object cache
typename ObjectCacheMap::iterator oitr = _objectCache.begin();
while (oitr != _objectCache.end())
{
if (oitr->second.mLastUsage <= expiryTime)
{
if (oitr->second.mValue != nullptr)
objectsToRemove.push_back(std::move(oitr->second.mValue));
_objectCache.erase(oitr++);
}
else
++oitr;
}
std::erase_if(_objectCache, [&](auto& v) {
Item& item = v.second;
if ((item.mValue != nullptr && item.mValue->referenceCount() > 1) || item.mLastUsage == 0)
item.mLastUsage = referenceTime;
if (item.mLastUsage > expiryTime)
return false;
if (item.mValue != nullptr)
objectsToRemove.push_back(std::move(item.mValue));
return true;
});
}
// note, actual unref happens outside of the lock
objectsToRemove.clear();

View file

@ -49,11 +49,7 @@ namespace Resource
virtual ~GenericResourceManager() = default;
/// Clear cache entries that have not been referenced for longer than expiryDelay.
void updateCache(double referenceTime) override
{
mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay);
}
void updateCache(double referenceTime) override { mCache->update(referenceTime, mExpiryDelay); }
/// Clear all cache entries.
void clearCache() override { mCache->clear(); }