coins: introduce CCoinsViewCache::ResetGuard
CCoinsViewCache::CreateResetGuard returns a guard that calls Reset on the cache when the guard goes out of scope. This RAII pattern ensures the cache is always properly reset when it leaves current scope. Co-authored-by: l0rinc <pap.lorinc@gmail.com> Co-authored-by: sedited <seb.kung@gmail.com>
This commit is contained in:
4 files changed
+85
No files matched your search
+20
@@ -6,6 +6,7 @@
|
|||||||
#ifndef BITCOIN_COINS_H
|
#ifndef BITCOIN_COINS_H
|
||||||
#define BITCOIN_COINS_H
|
#define BITCOIN_COINS_H
|
||||||
|
|
||||||
|
#include <attributes.h>
|
||||||
#include <compressor.h>
|
#include <compressor.h>
|
||||||
#include <core_memusage.h>
|
#include <core_memusage.h>
|
||||||
#include <memusage.h>
|
#include <memusage.h>
|
||||||
@@ -483,6 +484,25 @@ public:
|
|||||||
//! Run an internal sanity check on the cache data structure. */
|
//! Run an internal sanity check on the cache data structure. */
|
||||||
void SanityCheck() const;
|
void SanityCheck() const;
|
||||||
|
|
||||||
|
class ResetGuard
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
friend CCoinsViewCache;
|
||||||
|
CCoinsViewCache& m_cache;
|
||||||
|
explicit ResetGuard(CCoinsViewCache& cache LIFETIMEBOUND) noexcept : m_cache{cache} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ResetGuard(const ResetGuard&) = delete;
|
||||||
|
ResetGuard& operator=(const ResetGuard&) = delete;
|
||||||
|
ResetGuard(ResetGuard&&) = delete;
|
||||||
|
ResetGuard& operator=(ResetGuard&&) = delete;
|
||||||
|
|
||||||
|
~ResetGuard() { m_cache.Reset(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Create a scoped guard that will call `Reset()` on this cache when it goes out of scope.
|
||||||
|
[[nodiscard]] ResetGuard CreateResetGuard() noexcept { return ResetGuard{*this}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @note this is marked const, but may actually append to `cacheCoins`, increasing
|
* @note this is marked const, but may actually append to `cacheCoins`, increasing
|
||||||
|
|||||||
@@ -1120,4 +1120,47 @@ BOOST_AUTO_TEST_CASE(ccoins_emplace_duplicate_keeps_usage_balanced)
|
|||||||
BOOST_CHECK(cache.AccessCoin(outpoint) == coin1);
|
BOOST_CHECK(cache.AccessCoin(outpoint) == coin1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ccoins_reset_guard)
|
||||||
|
{
|
||||||
|
CCoinsViewTest root{m_rng};
|
||||||
|
CCoinsViewCache root_cache{&root};
|
||||||
|
uint256 base_best_block{m_rng.rand256()};
|
||||||
|
root_cache.SetBestBlock(base_best_block);
|
||||||
|
root_cache.Flush();
|
||||||
|
|
||||||
|
CCoinsViewCache cache{&root};
|
||||||
|
|
||||||
|
const COutPoint outpoint{Txid::FromUint256(m_rng.rand256()), m_rng.rand32()};
|
||||||
|
|
||||||
|
const Coin coin{CTxOut{m_rng.randrange(10), CScript{} << m_rng.randbytes(CScriptBase::STATIC_SIZE + 1)}, 1, false};
|
||||||
|
cache.EmplaceCoinInternalDANGER(COutPoint{outpoint}, Coin{coin});
|
||||||
|
|
||||||
|
uint256 cache_best_block{m_rng.rand256()};
|
||||||
|
cache.SetBestBlock(cache_best_block);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto reset_guard{cache.CreateResetGuard()};
|
||||||
|
BOOST_CHECK(cache.AccessCoin(outpoint) == coin);
|
||||||
|
BOOST_CHECK(!cache.AccessCoin(outpoint).IsSpent());
|
||||||
|
BOOST_CHECK_EQUAL(cache.GetCacheSize(), 1);
|
||||||
|
BOOST_CHECK_EQUAL(cache.GetBestBlock(), cache_best_block);
|
||||||
|
BOOST_CHECK(!root_cache.HaveCoinInCache(outpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_CHECK(cache.AccessCoin(outpoint).IsSpent());
|
||||||
|
BOOST_CHECK_EQUAL(cache.GetCacheSize(), 0);
|
||||||
|
BOOST_CHECK_EQUAL(cache.GetBestBlock(), base_best_block);
|
||||||
|
BOOST_CHECK(!root_cache.HaveCoinInCache(outpoint));
|
||||||
|
|
||||||
|
// Using a reset guard again is idempotent
|
||||||
|
{
|
||||||
|
const auto reset_guard{cache.CreateResetGuard()};
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_CHECK(cache.AccessCoin(outpoint).IsSpent());
|
||||||
|
BOOST_CHECK_EQUAL(cache.GetCacheSize(), 0);
|
||||||
|
BOOST_CHECK_EQUAL(cache.GetBestBlock(), base_best_block);
|
||||||
|
BOOST_CHECK(!root_cache.HaveCoinInCache(outpoint));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
@@ -85,6 +85,20 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend
|
|||||||
if (is_db && best_block.IsNull()) best_block = uint256::ONE;
|
if (is_db && best_block.IsNull()) best_block = uint256::ONE;
|
||||||
coins_view_cache.SetBestBlock(best_block);
|
coins_view_cache.SetBestBlock(best_block);
|
||||||
},
|
},
|
||||||
|
[&] {
|
||||||
|
{
|
||||||
|
const auto reset_guard{coins_view_cache.CreateResetGuard()};
|
||||||
|
}
|
||||||
|
// Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite().
|
||||||
|
if (is_db) {
|
||||||
|
const uint256 best_block{ConsumeUInt256(fuzzed_data_provider)};
|
||||||
|
if (best_block.IsNull()) {
|
||||||
|
good_data = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
coins_view_cache.SetBestBlock(best_block);
|
||||||
|
}
|
||||||
|
},
|
||||||
[&] {
|
[&] {
|
||||||
Coin move_to;
|
Coin move_to;
|
||||||
(void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
|
(void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
|
||||||
|
|||||||
@@ -401,6 +401,14 @@ FUZZ_TARGET(coinscache_sim)
|
|||||||
caches.back()->Sync();
|
caches.back()->Sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[&]() { // Reset.
|
||||||
|
sim_caches[caches.size()].Wipe();
|
||||||
|
// Apply to real caches.
|
||||||
|
{
|
||||||
|
const auto reset_guard{caches.back()->CreateResetGuard()};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
[&]() { // GetCacheSize
|
[&]() { // GetCacheSize
|
||||||
(void)caches.back()->GetCacheSize();
|
(void)caches.back()->GetCacheSize();
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user