1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

LruCache: Make the LRU cache implementation thread safe

This commit is contained in:
Anthony Nicholls 2025-07-30 13:35:11 +01:00 committed by Anthony Nicholls
parent c1affc0a0e
commit 667b3fae86
3 changed files with 35 additions and 6 deletions

View file

@ -43,6 +43,8 @@ public:
template <typename Fn> template <typename Fn>
const Value& get (Key key, Fn&& fn) const Value& get (Key key, Fn&& fn)
{ {
std::unique_lock lock { *mutex };
if (const auto iter = map.find (key); iter != map.end()) if (const auto iter = map.find (key); iter != map.end())
{ {
list.erase (iter->second.listIterator); list.erase (iter->second.listIterator);
@ -50,6 +52,28 @@ public:
return iter->second.value; return iter->second.value;
} }
const auto localInsertionCounter = insertionCounter;
// There are two reasons we don't want to have the mutex locked while
// getting the value
// 1. If the operation itself results in a call to the same cache it
// would cause a deadlock.
// 2. The generation of the value is likely to be slow therefore we
// don't want to force other threads to wait on this operation
lock.unlock();
auto value = fn (key);
lock.lock();
// While the mutex was unlocked the value may have already been added to
// the cache. In this case we can skip placing the value at the front as
// it should've already recently been placed at the front. However, we
// still need to skip adding the value to the cache for a second time.
if (localInsertionCounter != insertionCounter)
if (const auto iter = map.find (key); iter != map.end())
return iter->second.value;
++insertionCounter;
while (list.size() >= maxEntries) while (list.size() >= maxEntries)
{ {
const auto toRemove = list.begin(); const auto toRemove = list.begin();
@ -57,7 +81,6 @@ public:
list.erase (toRemove); list.erase (toRemove);
} }
auto value = fn (key);
const auto mapIteratorPair = map.emplace (std::move (key), Pair { std::move (value), {} }); const auto mapIteratorPair = map.emplace (std::move (key), Pair { std::move (value), {} });
jassert (mapIteratorPair.second); jassert (mapIteratorPair.second);
@ -66,6 +89,13 @@ public:
return mapIteratorPair.first->second.value; return mapIteratorPair.first->second.value;
} }
void clear()
{
const std::scoped_lock lock { *mutex };
list.clear();
map.clear();
}
private: private:
struct Pair struct Pair
{ {
@ -80,6 +110,8 @@ private:
typename Pair::Map map; typename Pair::Map map;
typename Pair::List list; typename Pair::List list;
uint32_t insertionCounter{};
std::unique_ptr<std::mutex> mutex = std::make_unique<std::mutex>();
}; };
} // namespace juce } // namespace juce

View file

@ -479,7 +479,7 @@ RectangleListSpriteBatch::~RectangleListSpriteBatch()
void RectangleListSpriteBatch::release() void RectangleListSpriteBatch::release()
{ {
whiteRectangle = nullptr; whiteRectangle = nullptr;
spriteBatches = {}; spriteBatches.clear();
destinations.free(); destinations.free();
destinationsCapacity = 0; destinationsCapacity = 0;
} }

View file

@ -196,13 +196,11 @@ public:
//============================================================================== //==============================================================================
void reset() void reset()
{ {
const ScopedLock sl { lock }; cache.clear();
cache = {};
} }
const auto& get (const Font& font, const int glyphNumber) const auto& get (const Font& font, const int glyphNumber)
{ {
const ScopedLock sl { lock };
return cache.get (Key { font, glyphNumber }, [] (const auto& key) return cache.get (Key { font, glyphNumber }, [] (const auto& key)
{ {
auto fontHeight = key.font.getHeight(); auto fontHeight = key.font.getHeight();
@ -233,7 +231,6 @@ private:
}; };
LruCache<Key, std::vector<GlyphLayer>> cache; LruCache<Key, std::vector<GlyphLayer>> cache;
CriticalSection lock;
static GlyphCache*& getSingletonPointer() noexcept static GlyphCache*& getSingletonPointer() noexcept
{ {