Repository: bitcoin/bitcoin
File: src/coins.cpp (C++)

Code before:
                }
                cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
                if (!itUs->second.IsDirty()) {
                    CCoinsCacheEntry::SetDirty(*itUs, m_sentinel);
                    ++m_dirty_count;
                }
                // NOTE: It isn't safe to mark the coin as FRESH in the parent
                // cache. If it already existed and was spent in the parent
                // cache then marking it FRESH would prevent that spentness
                // from being flushed to the grandparent.
            }
        }
    }
    hashBlock = hashBlockIn;
}

void CCoinsViewCache::Flush(bool will_reuse_cache)
{
    auto cursor{CoinsViewCacheCursor(m_dirty_count, m_sentinel, cacheCoins, /*will_erase=*/true)};
    base->BatchWrite(cursor, hashBlock);
    cacheCoins.clear();
    if (will_reuse_cache) {
        ReallocateCache();
    }
    cachedCoinsUsage = 0;
    m_dirty_count = 0;
}

void CCoinsViewCache::Sync()
{
    auto cursor{CoinsViewCacheCursor(m_dirty_count, m_sentinel, cacheCoins, /*will_erase=*/false)};
    base->BatchWrite(cursor, hashBlock);
    Assume(m_dirty_count == 0);
    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");
    }
}

void CCoinsViewCache::Uncache(const COutPoint& hash)
{
    CCoinsMap::iterator it = cacheCoins.find(hash);
    if (it != cacheCoins.end() && !it->second.IsDirty() && !it->second.IsFresh()) {
        cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
        TRACEPOINT(utxocache, uncache,
               hash.hash.data(),
               (uint32_t)hash.n,
               (uint32_t)it->second.coin.nHeight,
               (int64_t)it->second.coin.out.nValue,
               (bool)it->second.coin.IsCoinBase());
        cacheCoins.erase(it);

Review comment (performance):
We call `Assume(m_dirty_count == 0);` in `ReallocateCache();` above, but only set `m_dirty_count = 0;` here. We should just `Assume(m_dirty_count == 0);` after `BatchWrite` like we do with `Sync`.

Code after:
                if (!itUs->second.IsDirty()) {
                    CCoinsCacheEntry::SetDirty(*itUs, m_sentinel);
                    ++m_dirty_count;
                }
                // NOTE: It isn't safe to mark the coin as FRESH in the parent
                // cache. If it already existed and was spent in the parent
                // cache then marking it FRESH would prevent that spentness
                // from being flushed to the grandparent.
            }
        }
    }
    SetBestBlock(hashBlockIn);
}

void CCoinsViewCache::Flush(bool reallocate_cache)
{
    auto cursor{CoinsViewCacheCursor(m_dirty_count, m_sentinel, cacheCoins, /*will_erase=*/true)};
    base->BatchWrite(cursor, hashBlock);
    Assume(m_dirty_count == 0);
    cacheCoins.clear();
    if (reallocate_cache) {
        ReallocateCache();
    }
    cachedCoinsUsage = 0;
}

void CCoinsViewCache::Sync()
{
    auto cursor{CoinsViewCacheCursor(m_dirty_count, m_sentinel, cacheCoins, /*will_erase=*/false)};
    base->BatchWrite(cursor, hashBlock);
    Assume(m_dirty_count == 0);
    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");
    }
}

void CCoinsViewCache::Reset() noexcept
{
    cacheCoins.clear();
    cachedCoinsUsage = 0;
    m_dirty_count = 0;
    SetBestBlock(uint256::ZERO);
}

void CCoinsViewCache::Uncache(const COutPoint& hash)
{
    CCoinsMap::iterator it = cacheCoins.find(hash);
    if (it != cacheCoins.end() && !it->second.IsDirty()) {
        cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
        TRACEPOINT(utxocache, uncache,