From a24e543af3b1aaa25803835bae54df2b1a743349 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 15 Jul 2026 09:30:20 -0400 Subject: [PATCH] fix: Allocate TaggedCache::getKeys() memory outside of lock (#7567) Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com> --- include/xrpl/basics/TaggedCache.ipp | 41 +++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index b79561c71a..447743a7b7 100644 --- a/include/xrpl/basics/TaggedCache.ipp +++ b/include/xrpl/basics/TaggedCache.ipp @@ -3,6 +3,9 @@ #include #include // IWYU pragma: keep #include +#include + +#include namespace xrpl { @@ -601,8 +604,42 @@ TaggedCache v; { - std::scoped_lock const lock(mutex_); - v.reserve(cache_.size()); + // 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.) + std::size_t allocationIterations = 0; + std::unique_lock lock(mutex_); + for (auto size = cache_.size(); v.capacity() < size && allocationIterations < 20; + size = cache_.size()) + { + ScopeUnlock const unlock(lock); + if (allocationIterations > 0) + { + JLOG(journal_.info()) + << "getKeys(): Cache grew beyond allocated capacity after " + << allocationIterations << " prior attempt(s). Have " << v.capacity() + << ", need " << size << ". Retrying allocation"; + } + // 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. + constexpr std::size_t baseShift = 5; + auto const bufferOffset = std::min(allocationIterations, std::size_t{baseShift}); + auto const bufferShift = baseShift - bufferOffset; + size += (size >> bufferShift) + 1; + v.reserve(size); + ++allocationIterations; + } + 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( + v.capacity() >= cache_.size(), "xrpl::TaggedCache::getKeys(): sufficient capacity"); for (auto const& _ : cache_) v.push_back(_.first); }