1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-28 19:21:04 +00:00

Merge branch 'generic_object_cache' into 'master'

Refactor GenericObjectCache and add unit tests

See merge request OpenMW/openmw!3689
This commit is contained in:
Evil Eye 2023-12-29 21:22:30 +00:00
commit 01eb333fad
4 changed files with 414 additions and 96 deletions

View File

@ -95,6 +95,8 @@ file(GLOB UNITTEST_SRC_FILES
nifosg/testnifloader.cpp nifosg/testnifloader.cpp
esmterrain/testgridsampling.cpp esmterrain/testgridsampling.cpp
resource/testobjectcache.cpp
) )
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})

View File

@ -0,0 +1,349 @@
#include <components/resource/objectcache.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <osg/Object>
namespace Resource
{
namespace
{
using namespace ::testing;
TEST(ResourceGenericObjectCacheTest, getRefFromObjectCacheShouldReturnNullptrByDefault)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
EXPECT_EQ(cache->getRefFromObjectCache(42), nullptr);
}
TEST(ResourceGenericObjectCacheTest, getRefFromObjectCacheOrNoneShouldReturnNulloptByDefault)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(42), std::nullopt);
}
struct Object : osg::Object
{
Object() = default;
Object(const Object& other, const osg::CopyOp& copyOp = osg::CopyOp())
: osg::Object(other, copyOp)
{
}
META_Object(ResourceTest, Object)
};
TEST(ResourceGenericObjectCacheTest, shouldStoreValues)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const int key = 42;
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
EXPECT_EQ(cache->getRefFromObjectCache(key), value);
}
TEST(ResourceGenericObjectCacheTest, shouldStoreNullptrValues)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const int key = 42;
cache->addEntryToObjectCache(key, nullptr);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(nullptr));
}
TEST(ResourceGenericObjectCacheTest, updateShouldExtendLifetimeForItemsWithZeroTimestamp)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const int key = 42;
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value, 0);
value = nullptr;
const double referenceTime = 1000;
const double expiryDelay = 1;
cache->update(referenceTime, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
}
TEST(ResourceGenericObjectCacheTest, addEntryToObjectCacheShouldReplaceExistingItemByKey)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const int key = 42;
osg::ref_ptr<Object> value1(new Object);
osg::ref_ptr<Object> value2(new Object);
cache->addEntryToObjectCache(key, value1);
ASSERT_EQ(cache->getRefFromObjectCache(key), value1);
cache->addEntryToObjectCache(key, value2);
EXPECT_EQ(cache->getRefFromObjectCache(key), value2);
}
TEST(ResourceGenericObjectCacheTest, addEntryToObjectCacheShouldMarkLifetime)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const double referenceTime = 1;
const double expiryDelay = 2;
const int key = 42;
cache->addEntryToObjectCache(key, nullptr, referenceTime + expiryDelay);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + 2 * expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, updateShouldRemoveExpiredItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const double referenceTime = 1;
const double expiryDelay = 1;
const int key = 42;
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
value = nullptr;
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, updateShouldKeepExternallyReferencedItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const double referenceTime = 1;
const double expiryDelay = 1;
const int key = 42;
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(value));
}
TEST(ResourceGenericObjectCacheTest, updateShouldKeepNotExpiredItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const double referenceTime = 1;
const double expiryDelay = 2;
const int key = 42;
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
value = nullptr;
cache->update(referenceTime + expiryDelay, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay / 2, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
}
TEST(ResourceGenericObjectCacheTest, updateShouldKeepNotExpiredNullptrItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const double referenceTime = 1;
const double expiryDelay = 2;
const int key = 42;
cache->addEntryToObjectCache(key, nullptr);
cache->update(referenceTime + expiryDelay, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay / 2, expiryDelay);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
}
TEST(ResourceGenericObjectCacheTest, getRefFromObjectCacheOrNoneShouldNotExtendItemLifetime)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const double referenceTime = 1;
const double expiryDelay = 2;
const int key = 42;
cache->addEntryToObjectCache(key, nullptr);
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay / 2, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, lowerBoundShouldSupportHeterogeneousLookup)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
cache->addEntryToObjectCache("a", nullptr);
cache->addEntryToObjectCache("c", nullptr);
EXPECT_THAT(cache->lowerBound(std::string_view("b")), Optional(Pair("c", _)));
}
TEST(ResourceGenericObjectCacheTest, shouldSupportRemovingItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const int key = 42;
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
ASSERT_EQ(cache->getRefFromObjectCache(key), value);
cache->removeFromObjectCache(key);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, clearShouldRemoveAllItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
const int key1 = 42;
const int key2 = 13;
osg::ref_ptr<Object> value1(new Object);
osg::ref_ptr<Object> value2(new Object);
cache->addEntryToObjectCache(key1, value1);
cache->addEntryToObjectCache(key2, value2);
ASSERT_EQ(cache->getRefFromObjectCache(key1), value1);
ASSERT_EQ(cache->getRefFromObjectCache(key2), value2);
cache->clear();
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key1), std::nullopt);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key2), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, callShouldIterateOverAllItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
osg::ref_ptr<Object> value1(new Object);
osg::ref_ptr<Object> value2(new Object);
osg::ref_ptr<Object> value3(new Object);
cache->addEntryToObjectCache(1, value1);
cache->addEntryToObjectCache(2, value2);
cache->addEntryToObjectCache(3, value3);
std::vector<std::pair<int, osg::Object*>> actual;
cache->call([&](int key, osg::Object* value) { actual.emplace_back(key, value); });
EXPECT_THAT(actual, ElementsAre(Pair(1, value1.get()), Pair(2, value2.get()), Pair(3, value3.get())));
}
TEST(ResourceGenericObjectCacheTest, getCacheSizeShouldReturnNumberOrAddedItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
osg::ref_ptr<Object> value1(new Object);
osg::ref_ptr<Object> value2(new Object);
cache->addEntryToObjectCache(13, value1);
cache->addEntryToObjectCache(42, value2);
EXPECT_EQ(cache->getCacheSize(), 2);
}
TEST(ResourceGenericObjectCacheTest, lowerBoundShouldReturnFirstNotLessThatGivenKey)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
osg::ref_ptr<Object> value1(new Object);
osg::ref_ptr<Object> value2(new Object);
osg::ref_ptr<Object> value3(new Object);
cache->addEntryToObjectCache(1, value1);
cache->addEntryToObjectCache(2, value2);
cache->addEntryToObjectCache(4, value3);
EXPECT_THAT(cache->lowerBound(3), Optional(Pair(4, value3)));
}
TEST(ResourceGenericObjectCacheTest, lowerBoundShouldReturnNulloptWhenKeyIsGreaterThanAnyOther)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
osg::ref_ptr<Object> value1(new Object);
osg::ref_ptr<Object> value2(new Object);
osg::ref_ptr<Object> value3(new Object);
cache->addEntryToObjectCache(1, value1);
cache->addEntryToObjectCache(2, value2);
cache->addEntryToObjectCache(3, value3);
EXPECT_EQ(cache->lowerBound(4), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, addEntryToObjectCacheShouldSupportHeterogeneousLookup)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
const std::string key = "key";
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(std::string_view("key"), value);
EXPECT_EQ(cache->getRefFromObjectCache(key), value);
}
TEST(ResourceGenericObjectCacheTest, addEntryToObjectCacheShouldKeyMoving)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
std::string key(128, 'a');
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(std::move(key), value);
EXPECT_EQ(key, "");
EXPECT_EQ(cache->getRefFromObjectCache(std::string(128, 'a')), value);
}
TEST(ResourceGenericObjectCacheTest, removeFromObjectCacheShouldSupportHeterogeneousLookup)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
const std::string key = "key";
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
ASSERT_EQ(cache->getRefFromObjectCache(key), value);
cache->removeFromObjectCache(std::string_view("key"));
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
}
TEST(ResourceGenericObjectCacheTest, getRefFromObjectCacheShouldSupportHeterogeneousLookup)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
const std::string key = "key";
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
EXPECT_EQ(cache->getRefFromObjectCache(std::string_view("key")), value);
}
TEST(ResourceGenericObjectCacheTest, getRefFromObjectCacheOrNoneShouldSupportHeterogeneousLookup)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
const std::string key = "key";
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
EXPECT_THAT(cache->getRefFromObjectCacheOrNone(std::string_view("key")), Optional(value));
}
TEST(ResourceGenericObjectCacheTest, checkInObjectCacheShouldSupportHeterogeneousLookup)
{
osg::ref_ptr<GenericObjectCache<std::string>> cache(new GenericObjectCache<std::string>);
const std::string key = "key";
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(key, value);
EXPECT_TRUE(cache->checkInObjectCache(std::string_view("key"), 0));
}
}
}

View File

@ -24,6 +24,7 @@
#include <osg/Referenced> #include <osg/Referenced>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <algorithm>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
@ -48,48 +49,25 @@ namespace Resource
{ {
} }
/** For each object in the cache which has an reference count greater than 1 // Update last usage timestamp using referenceTime for each cache time if they are not nullptr and referenced
* (and therefore referenced by elsewhere in the application) set the time stamp // from somewhere else. Remove items with last usage > expiryTime. Note: last usage might be updated from other
* for that object in the cache to specified time. // places so nullptr or not references elsewhere items are not always removed.
* This would typically be called once per frame by applications which are doing database paging, void update(double referenceTime, double expiryDelay)
* 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)
{ {
std::vector<osg::ref_ptr<osg::Object>> objectsToRemove; std::vector<osg::ref_ptr<osg::Object>> objectsToRemove;
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); const double expiryTime = referenceTime - expiryDelay;
// Remove expired entries from object cache std::lock_guard<std::mutex> lock(mMutex);
typename ObjectCacheMap::iterator oitr = _objectCache.begin(); std::erase_if(mItems, [&](auto& v) {
while (oitr != _objectCache.end()) Item& item = v.second;
{ if ((item.mValue != nullptr && item.mValue->referenceCount() > 1) || item.mLastUsage == 0)
if (oitr->second.mLastUsage <= expiryTime) item.mLastUsage = referenceTime;
{ if (item.mLastUsage > expiryTime)
if (oitr->second.mValue != nullptr) return false;
objectsToRemove.push_back(std::move(oitr->second.mValue)); if (item.mValue != nullptr)
_objectCache.erase(oitr++); objectsToRemove.push_back(std::move(item.mValue));
} return true;
else });
++oitr;
}
} }
// note, actual unref happens outside of the lock // note, actual unref happens outside of the lock
objectsToRemove.clear(); objectsToRemove.clear();
@ -98,52 +76,57 @@ namespace Resource
/** Remove all objects in the cache regardless of having external references or expiry times.*/ /** Remove all objects in the cache regardless of having external references or expiry times.*/
void clear() void clear()
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
_objectCache.clear(); mItems.clear();
} }
/** Add a key,object,timestamp triple to the Registry::ObjectCache.*/ /** Add a key,object,timestamp triple to the Registry::ObjectCache.*/
void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0) template <class K>
void addEntryToObjectCache(K&& key, osg::Object* object, double timestamp = 0.0)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
_objectCache[key] = Item{ object, timestamp }; const auto it = mItems.find(key);
if (it == mItems.end())
mItems.emplace_hint(it, std::forward<K>(key), Item{ object, timestamp });
else
it->second = Item{ object, timestamp };
} }
/** Remove Object from cache.*/ /** Remove Object from cache.*/
void removeFromObjectCache(const KeyType& key) void removeFromObjectCache(const auto& key)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
typename ObjectCacheMap::iterator itr = _objectCache.find(key); const auto itr = mItems.find(key);
if (itr != _objectCache.end()) if (itr != mItems.end())
_objectCache.erase(itr); mItems.erase(itr);
} }
/** Get an ref_ptr<Object> from the object cache*/ /** Get an ref_ptr<Object> from the object cache*/
osg::ref_ptr<osg::Object> getRefFromObjectCache(const KeyType& key) osg::ref_ptr<osg::Object> getRefFromObjectCache(const auto& key)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
typename ObjectCacheMap::iterator itr = _objectCache.find(key); const auto itr = mItems.find(key);
if (itr != _objectCache.end()) if (itr != mItems.end())
return itr->second.mValue; return itr->second.mValue;
else else
return nullptr; return nullptr;
} }
std::optional<osg::ref_ptr<osg::Object>> getRefFromObjectCacheOrNone(const KeyType& key) std::optional<osg::ref_ptr<osg::Object>> getRefFromObjectCacheOrNone(const auto& key)
{ {
const std::lock_guard<std::mutex> lock(_objectCacheMutex); const std::lock_guard<std::mutex> lock(mMutex);
const auto it = _objectCache.find(key); const auto it = mItems.find(key);
if (it == _objectCache.end()) if (it == mItems.end())
return std::nullopt; return std::nullopt;
return it->second.mValue; return it->second.mValue;
} }
/** Check if an object is in the cache, and if it is, update its usage time stamp. */ /** Check if an object is in the cache, and if it is, update its usage time stamp. */
bool checkInObjectCache(const KeyType& key, double timeStamp) bool checkInObjectCache(const auto& key, double timeStamp)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
typename ObjectCacheMap::iterator itr = _objectCache.find(key); const auto itr = mItems.find(key);
if (itr != _objectCache.end()) if (itr != mItems.end())
{ {
itr->second.mLastUsage = timeStamp; itr->second.mLastUsage = timeStamp;
return true; return true;
@ -155,51 +138,43 @@ namespace Resource
/** call releaseGLObjects on all objects attached to the object cache.*/ /** call releaseGLObjects on all objects attached to the object cache.*/
void releaseGLObjects(osg::State* state) void releaseGLObjects(osg::State* state)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) for (const auto& [k, v] : mItems)
{ v.mValue->releaseGLObjects(state);
osg::Object* object = itr->second.mValue.get();
object->releaseGLObjects(state);
}
} }
/** call node->accept(nv); for all nodes in the objectCache. */ /** call node->accept(nv); for all nodes in the objectCache. */
void accept(osg::NodeVisitor& nv) void accept(osg::NodeVisitor& nv)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) for (const auto& [k, v] : mItems)
{ if (osg::Object* const object = v.mValue.get())
if (osg::Object* object = itr->second.mValue.get()) if (osg::Node* const node = dynamic_cast<osg::Node*>(object))
{
osg::Node* node = dynamic_cast<osg::Node*>(object);
if (node)
node->accept(nv); node->accept(nv);
}
}
} }
/** call operator()(KeyType, osg::Object*) for each object in the cache. */ /** call operator()(KeyType, osg::Object*) for each object in the cache. */
template <class Functor> template <class Functor>
void call(Functor& f) void call(Functor&& f)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) for (const auto& [k, v] : mItems)
f(it->first, it->second.mValue.get()); f(k, v.mValue.get());
} }
/** Get the number of objects in the cache. */ /** Get the number of objects in the cache. */
unsigned int getCacheSize() const unsigned int getCacheSize() const
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(mMutex);
return _objectCache.size(); return mItems.size();
} }
template <class K> template <class K>
std::optional<std::pair<KeyType, osg::ref_ptr<osg::Object>>> lowerBound(K&& key) std::optional<std::pair<KeyType, osg::ref_ptr<osg::Object>>> lowerBound(K&& key)
{ {
const std::lock_guard<std::mutex> lock(_objectCacheMutex); const std::lock_guard<std::mutex> lock(mMutex);
const auto it = _objectCache.lower_bound(std::forward<K>(key)); const auto it = mItems.lower_bound(std::forward<K>(key));
if (it == _objectCache.end()) if (it == mItems.end())
return std::nullopt; return std::nullopt;
return std::pair(it->first, it->second.mValue); return std::pair(it->first, it->second.mValue);
} }
@ -211,12 +186,8 @@ namespace Resource
double mLastUsage; double mLastUsage;
}; };
virtual ~GenericObjectCache() {} std::map<KeyType, Item, std::less<>> mItems;
mutable std::mutex mMutex;
using ObjectCacheMap = std::map<KeyType, Item, std::less<>>;
ObjectCacheMap _objectCache;
mutable std::mutex _objectCacheMutex;
}; };
class ObjectCache : public GenericObjectCache<std::string> class ObjectCache : public GenericObjectCache<std::string>

View File

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