@@ -108,6 +108,24 @@ using CoinsCachePair = std::pair<const COutPoint, CCoinsCacheEntry>;
108
108
struct CCoinsCacheEntry
109
109
{
110
110
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 };
111
129
uint8_t m_flags{0 };
112
130
113
131
public:
@@ -147,20 +165,45 @@ struct CCoinsCacheEntry
147
165
inline void AddFlags (uint8_t flags, CoinsCachePair& self, CoinsCachePair& sentinel) noexcept
148
166
{
149
167
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
+ }
150
174
m_flags |= flags;
151
175
}
152
176
inline void ClearFlags () noexcept
153
177
{
178
+ if (!m_flags) return ;
179
+ m_next->second .m_prev = m_prev;
180
+ m_prev->second .m_next = m_next;
154
181
m_flags = 0 ;
155
182
}
156
183
inline uint8_t GetFlags () const noexcept { return m_flags; }
157
184
inline bool IsDirty () const noexcept { return m_flags & DIRTY; }
158
185
inline bool IsFresh () const noexcept { return m_flags & FRESH; }
159
186
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
+
160
199
// ! Only use this for initializing the linked list sentinel
161
200
inline void SelfRef (CoinsCachePair& self) noexcept
162
201
{
163
202
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;
164
207
}
165
208
};
166
209
0 commit comments