mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
coins: move Sync logic to CoinsViewCacheCursor
Erase spent cache entries and clear flags of unspent entries inside the BatchWrite loop, instead of an additional loop after BatchWrite. Co-Authored-By: Pieter Wuille <pieter@wuille.net>
This commit is contained in:
parent
7825b8b9ae
commit
05cf4e1875
2 changed files with 38 additions and 11 deletions
|
@ -267,15 +267,10 @@ bool CCoinsViewCache::Sync()
|
|||
{
|
||||
auto cursor{CoinsViewCacheCursor(cachedCoinsUsage, m_sentinel, cacheCoins, /*will_erase=*/false)};
|
||||
bool fOk = base->BatchWrite(cursor, hashBlock);
|
||||
// Instead of clearing `cacheCoins` as we would in Flush(), just clear the
|
||||
// FRESH/DIRTY flags of any coin that isn't spent.
|
||||
for (auto it = cacheCoins.begin(); it != cacheCoins.end(); ) {
|
||||
if (it->second.coin.IsSpent()) {
|
||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
||||
it = cacheCoins.erase(it);
|
||||
} else {
|
||||
it->second.ClearFlags();
|
||||
++it;
|
||||
if (fOk) {
|
||||
if (m_sentinel.second.Next() != &m_sentinel) {
|
||||
/* BatchWrite must clear flags of all entries */
|
||||
throw std::logic_error("Not all unspent flagged entries were cleared");
|
||||
}
|
||||
}
|
||||
return fOk;
|
||||
|
|
36
src/coins.h
36
src/coins.h
|
@ -245,9 +245,26 @@ private:
|
|||
|
||||
/**
|
||||
* Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
|
||||
*
|
||||
* This is a helper struct to encapsulate the diverging logic between a non-erasing
|
||||
* CCoinsViewCache::Sync and an erasing CCoinsViewCache::Flush. This allows the receiver
|
||||
* of CCoinsView::BatchWrite to iterate through the flagged entries without knowing
|
||||
* the caller's intent.
|
||||
*
|
||||
* However, the receiver can still call CoinsViewCacheCursor::WillErase to see if the
|
||||
* caller will erase the entry after BatchWrite returns. If so, the receiver can
|
||||
* perform optimizations such as moving the coin out of the CCoinsCachEntry instead
|
||||
* of copying it.
|
||||
*/
|
||||
struct CoinsViewCacheCursor
|
||||
{
|
||||
//! If will_erase is not set, iterating through the cursor will erase spent coins from the map,
|
||||
//! and other coins will be unflagged (removing them from the linked list).
|
||||
//! If will_erase is set, the underlying map and linked list will not be modified,
|
||||
//! as the caller is expected to wipe the entire map anyway.
|
||||
//! This is an optimization compared to erasing all entries as the cursor iterates them when will_erase is set.
|
||||
//! Calling CCoinsMap::clear() afterwards is faster because a CoinsCachePair cannot be coerced back into a
|
||||
//! CCoinsMap::iterator to be erased, and must therefore be looked up again by key in the CCoinsMap before being erased.
|
||||
CoinsViewCacheCursor(size_t& usage LIFETIMEBOUND,
|
||||
CoinsCachePair& sentinel LIFETIMEBOUND,
|
||||
CCoinsMap& map LIFETIMEBOUND,
|
||||
|
@ -257,9 +274,24 @@ struct CoinsViewCacheCursor
|
|||
inline CoinsCachePair* Begin() const noexcept { return m_sentinel.second.Next(); }
|
||||
inline CoinsCachePair* End() const noexcept { return &m_sentinel; }
|
||||
|
||||
inline CoinsCachePair* NextAndMaybeErase(CoinsCachePair& current) noexcept { return current.second.Next(); }
|
||||
//! Return the next entry after current, possibly erasing current
|
||||
inline CoinsCachePair* NextAndMaybeErase(CoinsCachePair& current) noexcept
|
||||
{
|
||||
const auto next_entry{current.second.Next()};
|
||||
// If we are not going to erase the cache, we must still erase spent entries.
|
||||
// Otherwise clear the flags on the entry.
|
||||
if (!m_will_erase) {
|
||||
if (current.second.coin.IsSpent()) {
|
||||
m_usage -= current.second.coin.DynamicMemoryUsage();
|
||||
m_map.erase(current.first);
|
||||
} else {
|
||||
current.second.ClearFlags();
|
||||
}
|
||||
}
|
||||
return next_entry;
|
||||
}
|
||||
|
||||
inline bool WillErase(CoinsCachePair& current) const noexcept { return m_will_erase; }
|
||||
inline bool WillErase(CoinsCachePair& current) const noexcept { return m_will_erase || current.second.coin.IsSpent(); }
|
||||
private:
|
||||
size_t& m_usage;
|
||||
CoinsCachePair& m_sentinel;
|
||||
|
|
Loading…
Add table
Reference in a new issue