mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-30 10:30:22 +00:00
Make the getKeys() allocation more robust
- Make it very unlikely that the allocation loop will ever need to run
more than once. Recover gracefully if it does.
- Prevent livelock by limiting the total possible number of iterations.
- If this ever happens, throw our metaphorical hands in the air, and
allocate under lock.
- Pad the size before allocating to give the cache a little room to grow
while not holding the lock.
- Pad by less on each iteration.
- Assert that no more than two iterations occur. Even two is probably
overkill, but this allows for rare edge case scenarios. e.g. The
cache is very small, the allocation takes a long time (which is
contradictory) and a handful of items get added, overcoming the
padding.
This commit is contained in:
@@ -596,11 +596,33 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
std::vector<key_type> v;
|
||||
|
||||
{
|
||||
// Keep track of how many iterations are needed. Exit the loop if the number of retries gets
|
||||
// absurd. (Note that if this somehow ever happens, one more allocation will be done under
|
||||
// lock, which is undesirable, but really should be almost impossible. Also, assert that
|
||||
// there were fewer than 3 needed after the loop, because in a normal operating environment,
|
||||
// even 2 is going to be unusual, and 3 shouldn't be needed.
|
||||
std::size_t allocationIterations = 0;
|
||||
std::unique_lock lock(mutex_);
|
||||
for (auto size = cache_.size(); v.capacity() < size; size = cache_.size())
|
||||
for (auto size = cache_.size(); v.capacity() < size && allocationIterations < 20;
|
||||
size = cache_.size())
|
||||
{
|
||||
ScopeUnlock const unlock(lock);
|
||||
// Allocate the current size plus a little extra, in case the cache grows while
|
||||
// allocating. Each time another allocation is needed, the extra also gets bigger until
|
||||
// it ultimately doubles the size + 1.
|
||||
size += (size >> (4 - std::min(allocationIterations, 4ul))) + 1;
|
||||
v.reserve(size);
|
||||
++allocationIterations;
|
||||
}
|
||||
XRPL_ASSERT(
|
||||
allocationIterations < 3,
|
||||
"xrpl::TaggedCache::getKeys(): limited allocation iterations");
|
||||
if (v.capacity() < cache_.size())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::TaggedCache::getKeys(): failed to allocate sufficient capacity");
|
||||
v.reserve(cache_.size());
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
XRPL_ASSERT(lock.owns_lock(), "xrpl::TaggedCache::getKeys(): owns lock");
|
||||
XRPL_ASSERT(
|
||||
|
||||
Reference in New Issue
Block a user