LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_admin.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 188 318 59.1 %
Date: 2025-12-06 04:45:29 Functions: 14 21 66.7 %

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

Generated by: LCOV version 1.14