LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_admin.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 173 286 60.5 %
Date: 2025-10-27 04:40:00 Functions: 15 20 75.0 %

          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 : }

Generated by: LCOV version 1.14