13
13
bool CCoinsView::GetCoin (const COutPoint &outpoint, Coin &coin) const { return false ; }
14
14
uint256 CCoinsView::GetBestBlock () const { return uint256 (); }
15
15
std::vector<uint256> CCoinsView::GetHeadBlocks () const { return std::vector<uint256>(); }
16
- bool CCoinsView::BatchWrite (CCoinsMap &mapCoins, const uint256 &hashBlock) { return false ; }
16
+ bool CCoinsView::BatchWrite (CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase ) { return false ; }
17
17
std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor () const { return nullptr ; }
18
18
19
19
bool CCoinsView::HaveCoin (const COutPoint &outpoint) const
@@ -28,7 +28,7 @@ bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->
28
28
uint256 CCoinsViewBacked::GetBestBlock () const { return base->GetBestBlock (); }
29
29
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks () const { return base->GetHeadBlocks (); }
30
30
void CCoinsViewBacked::SetBackend (CCoinsView &viewIn) { base = &viewIn; }
31
- bool CCoinsViewBacked::BatchWrite (CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite (mapCoins, hashBlock); }
31
+ bool CCoinsViewBacked::BatchWrite (CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase ) { return base->BatchWrite (mapCoins, hashBlock, erase ); }
32
32
std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor () const { return base->Cursor (); }
33
33
size_t CCoinsViewBacked::EstimateSize () const { return base->EstimateSize (); }
34
34
@@ -176,8 +176,10 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
176
176
hashBlock = hashBlockIn;
177
177
}
178
178
179
- bool CCoinsViewCache::BatchWrite (CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
180
- for (CCoinsMap::iterator it = mapCoins.begin (); it != mapCoins.end (); it = mapCoins.erase (it)) {
179
+ bool CCoinsViewCache::BatchWrite (CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool erase) {
180
+ for (CCoinsMap::iterator it = mapCoins.begin ();
181
+ it != mapCoins.end ();
182
+ it = erase ? mapCoins.erase (it) : std::next (it)) {
181
183
// Ignore non-dirty entries (optimization).
182
184
if (!(it->second .flags & CCoinsCacheEntry::DIRTY)) {
183
185
continue ;
@@ -190,7 +192,14 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
190
192
// Create the coin in the parent cache, move the data up
191
193
// and mark it as dirty.
192
194
CCoinsCacheEntry& entry = cacheCoins[it->first ];
193
- entry.coin = std::move (it->second .coin );
195
+ if (erase) {
196
+ // The `move` call here is purely an optimization; we rely on the
197
+ // `mapCoins.erase` call in the `for` expression to actually remove
198
+ // the entry from the child map.
199
+ entry.coin = std::move (it->second .coin );
200
+ } else {
201
+ entry.coin = it->second .coin ;
202
+ }
194
203
cachedCoinsUsage += entry.coin .DynamicMemoryUsage ();
195
204
entry.flags = CCoinsCacheEntry::DIRTY;
196
205
// We can mark it FRESH in the parent if it was FRESH in the child
@@ -218,7 +227,14 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
218
227
} else {
219
228
// A normal modification.
220
229
cachedCoinsUsage -= itUs->second .coin .DynamicMemoryUsage ();
221
- itUs->second .coin = std::move (it->second .coin );
230
+ if (erase) {
231
+ // The `move` call here is purely an optimization; we rely on the
232
+ // `mapCoins.erase` call in the `for` expression to actually remove
233
+ // the entry from the child map.
234
+ itUs->second .coin = std::move (it->second .coin );
235
+ } else {
236
+ itUs->second .coin = it->second .coin ;
237
+ }
222
238
cachedCoinsUsage += itUs->second .coin .DynamicMemoryUsage ();
223
239
itUs->second .flags |= CCoinsCacheEntry::DIRTY;
224
240
// NOTE: It isn't safe to mark the coin as FRESH in the parent
@@ -233,12 +249,29 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
233
249
}
234
250
235
251
bool CCoinsViewCache::Flush () {
236
- bool fOk = base->BatchWrite (cacheCoins, hashBlock);
252
+ bool fOk = base->BatchWrite (cacheCoins, hashBlock, /* erase= */ true );
237
253
cacheCoins.clear ();
238
254
cachedCoinsUsage = 0 ;
239
255
return fOk ;
240
256
}
241
257
258
+ bool CCoinsViewCache::Sync ()
259
+ {
260
+ bool fOk = base->BatchWrite (cacheCoins, hashBlock, /* erase=*/ false );
261
+ // Instead of clearing `cacheCoins` as we would in Flush(), just clear the
262
+ // FRESH/DIRTY flags of any coin that isn't spent.
263
+ for (auto it = cacheCoins.begin (); it != cacheCoins.end (); ) {
264
+ if (it->second .coin .IsSpent ()) {
265
+ cachedCoinsUsage -= it->second .coin .DynamicMemoryUsage ();
266
+ it = cacheCoins.erase (it);
267
+ } else {
268
+ it->second .flags = 0 ;
269
+ ++it;
270
+ }
271
+ }
272
+ return fOk ;
273
+ }
274
+
242
275
void CCoinsViewCache::Uncache (const COutPoint& hash)
243
276
{
244
277
CCoinsMap::iterator it = cacheCoins.find (hash);
0 commit comments