Skip to content

Commit 24ce37c

Browse files
andrewtothsipal0rinc
committed
coins: track flagged cache entries in linked list
No visible behavior change. This commit tracks the flagged entries internally but the list is not iterated by anything. Co-Authored-By: Pieter Wuille <[email protected]> Co-Authored-By: l0rinc <[email protected]>
1 parent 58b7ed1 commit 24ce37c

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

src/coins.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ using CoinsCachePair = std::pair<const COutPoint, CCoinsCacheEntry>;
108108
struct CCoinsCacheEntry
109109
{
110110
private:
111+
/**
112+
* These are used to create a doubly linked list of flagged entries.
113+
* They are set in AddFlags and unset in ClearFlags.
114+
* A flagged entry is any entry that is either DIRTY, FRESH, or both.
115+
*
116+
* DIRTY entries are tracked so that only modified entries can be passed to
117+
* the parent cache for batch writing. This is a performance optimization
118+
* compared to giving all entries in the cache to the parent and having the
119+
* parent scan for only modified entries.
120+
*
121+
* FRESH-but-not-DIRTY coins can not occur in practice, since that would
122+
* mean a spent coin exists in the parent CCoinsView and not in the child
123+
* CCoinsViewCache. Nevertheless, if a spent coin is retrieved from the
124+
* parent cache, the FRESH-but-not-DIRTY coin will be tracked by the linked
125+
* list and deleted when Sync or Flush is called on the CCoinsViewCache.
126+
*/
127+
CoinsCachePair* m_prev{nullptr};
128+
CoinsCachePair* m_next{nullptr};
111129
uint8_t m_flags{0};
112130

113131
public:
@@ -147,20 +165,45 @@ struct CCoinsCacheEntry
147165
inline void AddFlags(uint8_t flags, CoinsCachePair& self, CoinsCachePair& sentinel) noexcept
148166
{
149167
Assume(&self.second == this);
168+
if (!m_flags && flags) {
169+
m_prev = sentinel.second.m_prev;
170+
m_next = &sentinel;
171+
sentinel.second.m_prev = &self;
172+
m_prev->second.m_next = &self;
173+
}
150174
m_flags |= flags;
151175
}
152176
inline void ClearFlags() noexcept
153177
{
178+
if (!m_flags) return;
179+
m_next->second.m_prev = m_prev;
180+
m_prev->second.m_next = m_next;
154181
m_flags = 0;
155182
}
156183
inline uint8_t GetFlags() const noexcept { return m_flags; }
157184
inline bool IsDirty() const noexcept { return m_flags & DIRTY; }
158185
inline bool IsFresh() const noexcept { return m_flags & FRESH; }
159186

187+
//! Only call Next when this entry is DIRTY, FRESH, or both
188+
inline CoinsCachePair* Next() const noexcept {
189+
Assume(m_flags);
190+
return m_next;
191+
}
192+
193+
//! Only call Prev when this entry is DIRTY, FRESH, or both
194+
inline CoinsCachePair* Prev() const noexcept {
195+
Assume(m_flags);
196+
return m_prev;
197+
}
198+
160199
//! Only use this for initializing the linked list sentinel
161200
inline void SelfRef(CoinsCachePair& self) noexcept
162201
{
163202
Assume(&self.second == this);
203+
m_prev = &self;
204+
m_next = &self;
205+
// Set sentinel to DIRTY so we can call Next on it
206+
m_flags = DIRTY;
164207
}
165208
};
166209

0 commit comments

Comments
 (0)