Line data Source code
1 : #include "fd_progcache_admin.h"
2 :
3 : /* Algorithm to estimate size of cache metadata structures (rec_pool
4 : object pool and rec_map hashchain table).
5 :
6 : FIXME Carefully balance this */
7 :
8 : static ulong
9 : fd_progcache_est_rec_max1( ulong wksp_footprint,
10 3 : ulong mean_cache_entry_size ) {
11 3 : return wksp_footprint / mean_cache_entry_size;
12 3 : }
13 :
14 : ulong
15 : fd_progcache_est_rec_max( ulong wksp_footprint,
16 3 : ulong mean_cache_entry_size ) {
17 3 : ulong est = fd_progcache_est_rec_max1( wksp_footprint, mean_cache_entry_size );
18 3 : if( FD_UNLIKELY( est>(1UL<<31) ) ) FD_LOG_ERR(( "fd_progcache_est_rec_max(wksp_footprint=%lu,mean_cache_entry_size=%lu) failed: invalid parameters", wksp_footprint, mean_cache_entry_size ));
19 3 : return fd_ulong_max( est, 2048UL );
20 3 : }
21 :
22 : fd_progcache_admin_t *
23 : fd_progcache_admin_join( fd_progcache_admin_t * ljoin,
24 42 : void * shfunk ) {
25 42 : if( FD_UNLIKELY( !ljoin ) ) {
26 0 : FD_LOG_WARNING(( "NULL ljoin" ));
27 0 : return NULL;
28 0 : }
29 42 : if( FD_UNLIKELY( !shfunk ) ) {
30 0 : FD_LOG_WARNING(( "NULL shfunk" ));
31 0 : return NULL;
32 0 : }
33 :
34 42 : memset( ljoin, 0, sizeof(fd_progcache_admin_t) );
35 42 : if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk ) ) ) {
36 0 : FD_LOG_CRIT(( "fd_funk_join failed" ));
37 0 : }
38 :
39 42 : return ljoin;
40 42 : }
41 :
42 : void *
43 : fd_progcache_admin_leave( fd_progcache_admin_t * ljoin,
44 39 : void ** opt_shfunk ) {
45 39 : if( FD_UNLIKELY( !ljoin ) ) FD_LOG_CRIT(( "NULL ljoin" ));
46 :
47 39 : if( FD_UNLIKELY( !fd_funk_leave( ljoin->funk, opt_shfunk ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
48 :
49 39 : return ljoin;
50 39 : }
51 :
52 : /* Begin transaction-level operations. It is assumed that funk_txn data
53 : structures are not concurrently modified. This includes txn_pool and
54 : txn_map. */
55 :
56 : void
57 : fd_progcache_txn_attach_child( fd_progcache_admin_t * cache,
58 : fd_funk_txn_xid_t const * xid_parent,
59 78 : fd_funk_txn_xid_t const * xid_new ) {
60 78 : FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: created with parent %lu:%lu",
61 78 : (void *)cache->funk,
62 78 : xid_new ->ul[0], xid_new ->ul[1],
63 78 : xid_parent->ul[0], xid_parent->ul[1] ));
64 78 : fd_funk_txn_prepare( cache->funk, xid_parent, xid_new );
65 78 : }
66 :
67 : static void
68 : fd_progcache_txn_cancel_one( fd_progcache_admin_t * cache,
69 60 : fd_funk_txn_t * txn ) {
70 60 : FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: cancel", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
71 :
72 60 : fd_funk_t * funk = cache->funk;
73 60 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( txn->child_head_cidx ) ||
74 60 : !fd_funk_txn_idx_is_null( txn->child_tail_cidx ) ) ) {
75 0 : FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: txn at %p with xid %lu:%lu has children (data corruption?)",
76 0 : (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
77 0 : }
78 :
79 : /* Phase 1: Drain users from transaction */
80 :
81 60 : fd_rwlock_write( txn->lock );
82 60 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_CANCEL;
83 :
84 : /* Phase 2: Remove records */
85 :
86 111 : while( !fd_funk_rec_idx_is_null( txn->rec_head_idx ) ) {
87 51 : fd_funk_rec_t * rec = &funk->rec_pool->ele[ txn->rec_head_idx ];
88 51 : uint next_idx = rec->next_idx;
89 51 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
90 51 : if( FD_LIKELY( !fd_funk_rec_idx_is_null( next_idx ) ) ) {
91 0 : funk->rec_pool->ele[ next_idx ].prev_idx = FD_FUNK_REC_IDX_NULL;
92 0 : }
93 :
94 51 : fd_funk_val_flush( rec, funk->alloc, funk->wksp );
95 :
96 51 : fd_funk_rec_query_t query[1];
97 51 : int remove_err = fd_funk_rec_map_remove( funk->rec_map, &rec->pair, NULL, query, FD_MAP_FLAG_BLOCKING );
98 51 : if( FD_UNLIKELY( remove_err ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
99 :
100 51 : fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
101 :
102 51 : txn->rec_head_idx = next_idx;
103 51 : if( fd_funk_rec_idx_is_null( next_idx ) ) txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
104 51 : }
105 :
106 : /* Phase 3: Remove transaction from fork graph */
107 :
108 60 : uint self_cidx = fd_funk_txn_cidx( (ulong)( txn-funk->txn_pool->ele ) );
109 60 : uint prev_cidx = txn->sibling_prev_cidx; ulong prev_idx = fd_funk_txn_idx( prev_cidx );
110 60 : uint next_cidx = txn->sibling_next_cidx; ulong next_idx = fd_funk_txn_idx( next_cidx );
111 60 : if( !fd_funk_txn_idx_is_null( next_idx ) ) {
112 0 : funk->txn_pool->ele[ next_idx ].sibling_prev_cidx = prev_cidx;
113 0 : }
114 60 : if( !fd_funk_txn_idx_is_null( prev_idx ) ) {
115 0 : funk->txn_pool->ele[ prev_idx ].sibling_next_cidx = next_cidx;
116 0 : }
117 60 : if( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) {
118 30 : fd_funk_txn_t * parent = &funk->txn_pool->ele[ fd_funk_txn_idx( txn->parent_cidx ) ];
119 30 : if( parent->child_head_cidx==self_cidx ) parent->child_head_cidx = next_cidx;
120 30 : if( parent->child_tail_cidx==self_cidx ) parent->child_tail_cidx = prev_cidx;
121 30 : } else {
122 30 : if( funk->shmem->child_head_cidx==self_cidx ) funk->shmem->child_head_cidx = next_cidx;
123 30 : if( funk->shmem->child_tail_cidx==self_cidx ) funk->shmem->child_tail_cidx = prev_cidx;
124 30 : }
125 :
126 : /* Phase 4: Remove transcation from index */
127 :
128 60 : fd_funk_txn_map_query_t query[1];
129 60 : int remove_err = fd_funk_txn_map_remove( funk->txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
130 60 : if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
131 0 : FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: fd_funk_txn_map_remove(%lu:%lu) failed: %i-%s",
132 0 : txn->xid.ul[0], txn->xid.ul[1], remove_err, fd_map_strerror( remove_err ) ));
133 0 : }
134 :
135 : /* Phase 5: Free transaction object */
136 :
137 60 : fd_rwlock_unwrite( txn->lock );
138 60 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
139 60 : fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
140 60 : }
141 :
142 : /* Cancels txn and all children */
143 :
144 : static void
145 : fd_progcache_txn_cancel_tree( fd_progcache_admin_t * cache,
146 60 : fd_funk_txn_t * txn ) {
147 78 : for(;;) {
148 78 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
149 78 : if( fd_funk_txn_idx_is_null( child_idx ) ) break;
150 18 : fd_funk_txn_t * child = &cache->funk->txn_pool->ele[ child_idx ];
151 18 : fd_progcache_txn_cancel_tree( cache, child );
152 18 : }
153 60 : fd_progcache_txn_cancel_one( cache, txn );
154 60 : }
155 :
156 : /* Cancels all left/right siblings */
157 :
158 : static void
159 : fd_progcache_txn_cancel_prev_list( fd_progcache_admin_t * cache,
160 15 : fd_funk_txn_t * txn ) {
161 15 : ulong self_idx = (ulong)( txn - cache->funk->txn_pool->ele );
162 15 : for(;;) {
163 15 : ulong prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
164 15 : if( FD_UNLIKELY( prev_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
165 15 : if( fd_funk_txn_idx_is_null( prev_idx ) ) break;
166 0 : fd_funk_txn_t * sibling = &cache->funk->txn_pool->ele[ prev_idx ];
167 0 : fd_progcache_txn_cancel_tree( cache, sibling );
168 0 : }
169 15 : }
170 :
171 : static void
172 : fd_progcache_txn_cancel_next_list( fd_progcache_admin_t * cache,
173 57 : fd_funk_txn_t * txn ) {
174 57 : ulong self_idx = (ulong)( txn - cache->funk->txn_pool->ele );
175 57 : for(;;) {
176 57 : ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
177 57 : if( FD_UNLIKELY( next_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
178 57 : if( fd_funk_txn_idx_is_null( next_idx ) ) break;
179 0 : fd_funk_txn_t * sibling = &cache->funk->txn_pool->ele[ next_idx ];
180 0 : fd_progcache_txn_cancel_tree( cache, sibling );
181 0 : }
182 57 : }
183 :
184 : void
185 : fd_progcache_txn_cancel( fd_progcache_admin_t * cache,
186 42 : fd_funk_txn_xid_t const * xid ) {
187 42 : fd_funk_t * funk = cache->funk;
188 :
189 42 : fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
190 42 : if( FD_UNLIKELY( !txn ) ) {
191 0 : FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
192 0 : }
193 :
194 42 : fd_progcache_txn_cancel_next_list( cache, txn );
195 42 : fd_progcache_txn_cancel_tree( cache, txn );
196 42 : }
197 :
198 : /* fd_progcache_gc_root cleans up a stale "rooted" version of a
199 : record. */
200 :
201 : static void
202 : fd_progcache_gc_root( fd_progcache_admin_t * cache,
203 9 : fd_funk_xid_key_pair_t const * pair ) {
204 9 : fd_funk_t * funk = cache->funk;
205 :
206 : /* Phase 1: Remove record from map if found */
207 :
208 9 : fd_funk_rec_query_t query[1];
209 9 : int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
210 9 : if( rm_err==FD_MAP_ERR_KEY ) return;
211 6 : if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) ));
212 :
213 : /* Phase 2: Invalidate record */
214 :
215 6 : fd_funk_rec_t * old_rec = query->ele;
216 6 : memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
217 6 : FD_COMPILER_MFENCE();
218 :
219 : /* Phase 3: Free record */
220 :
221 6 : old_rec->map_next = FD_FUNK_REC_IDX_NULL;
222 6 : fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
223 6 : fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
224 6 : }
225 :
226 : /* fd_progcache_publish_recs publishes all of a progcache's records.
227 : It is assumed at this point that the txn has no more concurrent
228 : users. */
229 :
230 : static void
231 : fd_progcache_publish_recs( fd_progcache_admin_t * cache,
232 15 : fd_funk_txn_t * txn ) {
233 : /* Iterate record list */
234 15 : uint head = txn->rec_head_idx;
235 15 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
236 15 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
237 24 : while( !fd_funk_rec_idx_is_null( head ) ) {
238 9 : fd_funk_rec_t * rec = &cache->funk->rec_pool->ele[ head ];
239 :
240 : /* Evict previous value from hash chain */
241 9 : fd_funk_xid_key_pair_t pair[1];
242 9 : fd_funk_rec_key_copy( pair->key, rec->pair.key );
243 9 : fd_funk_txn_xid_set_root( pair->xid );
244 9 : fd_progcache_gc_root( cache, pair );
245 :
246 : /* Migrate record to root */
247 9 : uint next = rec->next_idx;
248 9 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
249 9 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
250 9 : fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } };
251 9 : fd_funk_txn_xid_st_atomic( rec->pair.xid, &root );
252 :
253 9 : head = next; /* next record*/
254 9 : }
255 15 : }
256 :
257 : /* fd_progcache_txn_publish_one merges an in-prep transaction whose
258 : parent is the last published, into the parent. */
259 :
260 : static void
261 : fd_progcache_txn_publish_one( fd_progcache_admin_t * cache,
262 15 : fd_funk_txn_xid_t const * xid ) {
263 15 : fd_funk_t * funk = cache->funk;
264 :
265 : /* Phase 1: Mark transaction as "last published" */
266 :
267 15 : fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
268 15 : if( FD_UNLIKELY( !txn ) ) {
269 0 : FD_LOG_CRIT(( "fd_progcache_publish failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
270 0 : }
271 15 : FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: publish", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
272 15 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
273 0 : FD_LOG_CRIT(( "fd_progcache_publish failed: txn with xid %lu:%lu is not a child of the last published txn", xid->ul[0], xid->ul[1] ));
274 0 : }
275 15 : fd_funk_txn_xid_st_atomic( funk->shmem->last_publish, xid );
276 :
277 : /* Phase 2: Drain users from transaction */
278 :
279 15 : fd_rwlock_write( txn->lock );
280 15 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_PUBLISH;
281 :
282 : /* Phase 3: Migrate records */
283 :
284 15 : fd_progcache_publish_recs( cache, txn );
285 :
286 : /* Phase 4: Remove transaction from fork graph
287 :
288 : Because the transaction has no more records, removing it from the
289 : fork graph has no visible side effects to concurrent query ops
290 : (always return "no found") or insert ops (refuse to write to a
291 : "publish" state txn). */
292 :
293 15 : { /* Adjust the parent pointers of the children to point to "last published" */
294 15 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
295 21 : while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
296 6 : funk->txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
297 6 : child_idx = fd_funk_txn_idx( funk->txn_pool->ele[ child_idx ].sibling_next_cidx );
298 6 : }
299 15 : }
300 :
301 : /* Phase 5: Remove transaction from index
302 :
303 : The transaction is now an orphan and won't get any new records. */
304 :
305 15 : fd_funk_txn_map_query_t query[1];
306 15 : int remove_err = fd_funk_txn_map_remove( funk->txn_map, xid, NULL, query, 0 );
307 15 : if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
308 0 : FD_LOG_CRIT(( "fd_progcache_publish failed: fd_funk_txn_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
309 0 : }
310 :
311 : /* Phase 6: Free transaction object */
312 :
313 15 : fd_rwlock_unwrite( txn->lock );
314 15 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
315 15 : txn->parent_cidx = UINT_MAX;
316 15 : txn->sibling_prev_cidx = UINT_MAX;
317 15 : txn->sibling_next_cidx = UINT_MAX;
318 15 : txn->child_head_cidx = UINT_MAX;
319 15 : txn->child_tail_cidx = UINT_MAX;
320 15 : fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
321 15 : }
322 :
323 : void
324 : fd_progcache_txn_advance_root( fd_progcache_admin_t * cache,
325 15 : fd_funk_txn_xid_t const * xid ) {
326 15 : fd_funk_t * funk = cache->funk;
327 :
328 15 : fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
329 15 : if( FD_UNLIKELY( !txn ) ) {
330 0 : FD_LOG_CRIT(( "fd_progcache_txn_advance_root failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
331 0 : }
332 :
333 15 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
334 0 : FD_LOG_CRIT(( "fd_progcache_txn_advance_root: parent of txn %lu:%lu is not root", xid->ul[0], xid->ul[1] ));
335 0 : }
336 :
337 15 : fd_progcache_txn_cancel_prev_list( cache, txn );
338 15 : fd_progcache_txn_cancel_next_list( cache, txn );
339 15 : txn->sibling_prev_cidx = UINT_MAX;
340 15 : txn->sibling_next_cidx = UINT_MAX;
341 :
342 : /* Children of transaction are now children of root */
343 15 : funk->shmem->child_head_cidx = txn->child_head_cidx;
344 15 : funk->shmem->child_tail_cidx = txn->child_tail_cidx;
345 :
346 15 : fd_progcache_txn_publish_one( cache, fd_funk_txn_xid( txn ) );
347 15 : }
348 :
349 : /* reset_txn_list does a depth-first traversal of the txn tree.
350 : Detaches all recs from txns by emptying rec linked lists. */
351 :
352 : static void
353 : reset_txn_list( fd_funk_t * funk,
354 0 : ulong txn_head_idx ) {
355 0 : fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
356 0 : for( ulong idx = txn_head_idx;
357 0 : !fd_funk_txn_idx_is_null( idx );
358 0 : ) {
359 0 : fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
360 0 : fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
361 0 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
362 0 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
363 0 : reset_txn_list( funk, txn->child_head_cidx );
364 0 : idx = fd_funk_txn_idx( txn->sibling_next_cidx );
365 0 : }
366 0 : }
367 :
368 : /* reset_rec_map frees all records in a funk instance. */
369 :
370 : static void
371 0 : reset_rec_map( fd_funk_t * funk ) {
372 0 : fd_wksp_t * wksp = funk->wksp;
373 0 : fd_alloc_t * alloc = funk->alloc;
374 0 : fd_funk_rec_map_t * rec_map = funk->rec_map;
375 0 : fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
376 :
377 0 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
378 0 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
379 0 : for(
380 0 : fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
381 0 : !fd_funk_rec_map_iter_done( iter );
382 0 : ) {
383 0 : fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( iter );
384 0 : ulong next = fd_funk_rec_map_private_idx( rec->map_next );;
385 :
386 : /* Remove rec object from map */
387 0 : fd_funk_rec_map_query_t rec_query[1];
388 0 : int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
389 0 : fd_funk_rec_key_t key; fd_funk_rec_key_copy( &key, rec->pair.key );
390 0 : if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
391 :
392 : /* Free rec resources */
393 0 : fd_funk_val_flush( rec, alloc, wksp );
394 0 : fd_funk_rec_pool_release( rec_pool, rec, 1 );
395 0 : iter.ele_idx = next;
396 0 : }
397 0 : }
398 0 : }
399 :
400 : void
401 0 : fd_progcache_reset( fd_progcache_admin_t * cache ) {
402 0 : fd_funk_t * funk = cache->funk;
403 0 : reset_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
404 0 : reset_rec_map( funk );
405 0 : }
406 :
407 : /* clear_txn_list does a depth-first traversal of the txn tree.
408 : Removes all txns. */
409 :
410 : static void
411 : clear_txn_list( fd_funk_t * funk,
412 0 : ulong txn_head_idx ) {
413 0 : fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
414 0 : fd_funk_txn_map_t * txn_map = funk->txn_map;
415 0 : for( ulong idx = txn_head_idx;
416 0 : !fd_funk_txn_idx_is_null( idx );
417 0 : ) {
418 0 : fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
419 0 : fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
420 0 : ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
421 0 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
422 0 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
423 0 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
424 0 : txn->child_head_cidx = UINT_MAX;
425 0 : txn->child_tail_cidx = UINT_MAX;
426 0 : txn->parent_cidx = UINT_MAX;
427 0 : txn->sibling_prev_cidx = UINT_MAX;
428 0 : txn->sibling_next_cidx = UINT_MAX;
429 0 : clear_txn_list( funk, child_idx );
430 0 : fd_funk_txn_map_query_t query[1];
431 0 : int rm_err = fd_funk_txn_map_remove( txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
432 0 : if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) ));
433 0 : txn->state = FD_FUNK_TXN_STATE_FREE;
434 0 : int free_err = fd_funk_txn_pool_release( txn_pool, txn, 1 );
435 0 : if( FD_UNLIKELY( free_err!=FD_POOL_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_pool_release failed (%i)", free_err ));
436 0 : idx = next_idx;
437 0 : }
438 0 : funk->shmem->child_head_cidx = UINT_MAX;
439 0 : funk->shmem->child_tail_cidx = UINT_MAX;
440 0 : }
441 :
442 : void
443 0 : fd_progcache_clear( fd_progcache_admin_t * cache ) {
444 0 : fd_funk_t * funk = cache->funk;
445 0 : clear_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
446 0 : reset_rec_map( funk );
447 0 : }
448 :
449 : void
450 39 : fd_progcache_verify( fd_progcache_admin_t * cache ) {
451 39 : FD_TEST( fd_funk_verify( cache->funk )==FD_FUNK_SUCCESS );
452 39 : }
|