@@ -108,6 +108,24 @@ using CoinsCachePair = std::pair<const COutPoint, CCoinsCacheEntry>;
108108struct CCoinsCacheEntry
109109{
110110private:
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
113131public:
@@ -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