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:
parent
c1affc0a0e
commit
667b3fae86
3 changed files with 35 additions and 6 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue