LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_admin.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 287 365 78.6 %
Date: 2026-06-29 05:51:35 Functions: 13 15 86.7 %

          Line data    Source code
       1             : #include "fd_progcache.h"
       2             : #include "fd_progcache_admin.h"
       3             : #include "fd_progcache_base.h"
       4             : #include "fd_progcache_clock.h"
       5             : #include "fd_progcache_rec.h"
       6             : #include "fd_progcache_reclaim.h"
       7             : #include "fd_progcache_xid.h"
       8             : #include "../../util/racesan/fd_racesan_target.h"
       9             : 
      10             : /* FIXME get rid of this thread-local */
      11             : FD_TL fd_progcache_admin_metrics_t fd_progcache_admin_metrics_g;
      12             : 
      13             : /* Algorithm to estimate size of cache metadata structures (rec_pool
      14             :    object pool and rec_map hashchain table).
      15             : 
      16             :    FIXME Carefully balance this */
      17             : 
      18             : static ulong
      19             : fd_progcache_est_rec_max1( ulong wksp_footprint,
      20           0 :                            ulong mean_cache_entry_size ) {
      21           0 :   return wksp_footprint / mean_cache_entry_size;
      22           0 : }
      23             : 
      24             : ulong
      25             : fd_progcache_est_rec_max( ulong wksp_footprint,
      26           0 :                           ulong mean_cache_entry_size ) {
      27           0 :   ulong est = fd_progcache_est_rec_max1( wksp_footprint, mean_cache_entry_size );
      28           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 ));
      29           0 :   return fd_ulong_max( est, 2048UL );
      30           0 : }
      31             : 
      32             : /* Begin transaction-level operations.  It is assumed that txn data
      33             :    structures are not concurrently modified.  This includes txn_pool and
      34             :    txn_map. */
      35             : 
      36             : fd_progcache_fork_id_t
      37             : fd_progcache_attach_child( fd_progcache_join_t *  cache,
      38        3801 :                            fd_progcache_fork_id_t parent_fork_id ) {
      39        3801 :   if( FD_UNLIKELY( !cache ) ) FD_LOG_CRIT(( "invalid arguments" ));
      40             : 
      41        3801 :   fd_rwlock_write( &cache->shmem->txn.rwlock );
      42        3801 :   if( FD_UNLIKELY( fd_prog_txnp_free( cache->txn.pool )==0UL ) ) {
      43           0 :     FD_LOG_ERR(( "fd_progcache_attach_child failed: transaction object pool out of memory" ));
      44           0 :   }
      45             : 
      46        3801 :   ulong  txn_max = fd_prog_txnp_max( cache->txn.pool );
      47        3801 :   ulong  parent_idx;
      48        3801 :   uint * _child_head_idx;
      49        3801 :   uint * _child_tail_idx;
      50             : 
      51        3801 :   fd_progcache_fork_id_t root = __atomic_load_n( &cache->shmem->txn.root, memory_order_relaxed );
      52        3801 :   if( FD_UNLIKELY( parent_fork_id == root ) ) {
      53             : 
      54        3687 :     parent_idx = FD_PROGCACHE_TXN_IDX_NULL;
      55             : 
      56        3687 :     _child_head_idx = &cache->shmem->txn.child_head_idx;
      57        3687 :     _child_tail_idx = &cache->shmem->txn.child_tail_idx;
      58             : 
      59        3687 :   } else {
      60             : 
      61         114 :     parent_idx = fd_prog_txnm_idx_query( cache->txn.map, &parent_fork_id, ULONG_MAX, cache->txn.pool );
      62         114 :     if( FD_UNLIKELY( parent_idx==ULONG_MAX ) ) {
      63           0 :       FD_LOG_CRIT(( "fd_progcache_attach_child failed: user provided invalid parent fork_id %lu", parent_fork_id ));
      64           0 :     }
      65         114 :     if( FD_UNLIKELY( parent_idx >= txn_max ) )
      66           0 :       FD_LOG_CRIT(( "progcache: corruption detected (attach_child parent_idx=%lu txn_max=%lu)", parent_idx, txn_max ));
      67             : 
      68         114 :     _child_head_idx = &cache->txn.pool[ parent_idx ].child_head_idx;
      69         114 :     _child_tail_idx = &cache->txn.pool[ parent_idx ].child_tail_idx;
      70             : 
      71         114 :   }
      72             : 
      73        3801 :   uint txn_idx = (uint)fd_prog_txnp_idx_acquire( cache->txn.pool );
      74        3801 :   if( FD_UNLIKELY( txn_idx==UINT_MAX ) ) FD_LOG_ERR(( "fd_progcache_attach_child failed: transaction object pool out of memory" ));
      75        3801 :   fd_progcache_txn_t * txn = &cache->txn.pool[ txn_idx ];
      76        3801 :   txn->xid = __atomic_add_fetch( &cache->shmem->txn.seq, 1UL, memory_order_relaxed );
      77             : 
      78        3801 :   uint sibling_prev_idx = *_child_tail_idx;
      79             : 
      80        3801 :   int first_born = sibling_prev_idx==UINT_MAX;
      81        3801 :   if( FD_UNLIKELY( !first_born && (ulong)sibling_prev_idx >= txn_max ) )
      82           0 :     FD_LOG_CRIT(( "progcache: corruption detected (attach_child sibling_prev_idx=%u txn_max=%lu)", sibling_prev_idx, txn_max ));
      83             : 
      84        3801 :   txn->parent_idx       = (uint)parent_idx;
      85        3801 :   txn->child_head_idx   = UINT_MAX;
      86        3801 :   txn->child_tail_idx   = UINT_MAX;
      87        3801 :   txn->sibling_prev_idx = (uint)sibling_prev_idx;
      88        3801 :   txn->sibling_next_idx = UINT_MAX;
      89             : 
      90        3801 :   txn->rec_head_idx = UINT_MAX;
      91        3801 :   txn->rec_tail_idx = UINT_MAX;
      92             : 
      93             :   /* TODO: consider branchless impl */
      94        3801 :   if( FD_LIKELY( first_born ) ) *_child_head_idx            = (uint)txn_idx; /* opt for non-compete */
      95           9 :   else cache->txn.pool[ sibling_prev_idx ].sibling_next_idx = (uint)txn_idx;
      96             : 
      97        3801 :   *_child_tail_idx = (uint)txn_idx;
      98             : 
      99        3801 :   fd_prog_txnm_idx_insert( cache->txn.map, txn_idx, cache->txn.pool );
     100             : 
     101        3801 :   fd_rwlock_unwrite( &cache->shmem->txn.rwlock );
     102        3801 :   return txn->xid;
     103        3801 : }
     104             : 
     105             : static void
     106             : fd_progcache_cancel_one( fd_progcache_join_t * cache,
     107         123 :                          fd_progcache_txn_t *  txn ) {
     108         123 :   ulong rec_max = fd_prog_recp_ele_max( cache->rec.pool );
     109         123 :   ulong txn_max = fd_prog_txnp_max( cache->txn.pool );
     110             : 
     111         123 :   fd_rwlock_write( &txn->lock );
     112             : 
     113         123 :   if( FD_UNLIKELY( txn->child_head_idx!=UINT_MAX ||
     114         123 :                    txn->child_tail_idx!=UINT_MAX ) ) {
     115           0 :     FD_LOG_CRIT(( "fd_progcache_cancel failed: txn at %p with fork_id %lu has children (data corruption?)",
     116           0 :                   (void *)txn, txn->xid ));
     117           0 :   }
     118             : 
     119             :   /* Remove records */
     120             : 
     121         183 :   for( uint idx = txn->rec_head_idx; idx!=UINT_MAX; idx = cache->rec.pool->ele[ idx ].next_idx ) {
     122          60 :     if( FD_UNLIKELY( (ulong)idx >= rec_max ) )
     123           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_one rec_idx=%u rec_max=%lu)", idx, rec_max ));
     124          60 :     atomic_store_explicit( &cache->rec.pool->ele[ idx ].txn_idx, UINT_MAX, memory_order_release );
     125          60 :     fd_racesan_hook( "prog_cancel_one:post_orphan" );
     126          60 :     fd_prog_delete_rec( cache, &cache->rec.pool->ele[ idx ] );
     127          60 :   }
     128             : 
     129         123 :   txn->rec_head_idx = UINT_MAX;
     130         123 :   txn->rec_tail_idx = UINT_MAX;
     131             : 
     132             :   /* Remove transaction from fork graph */
     133             : 
     134         123 :   uint self_idx = (uint)( txn - cache->txn.pool );
     135         123 :   uint prev_idx = txn->sibling_prev_idx;
     136         123 :   uint next_idx = txn->sibling_next_idx;
     137         123 :   if( next_idx!=UINT_MAX ) {
     138           0 :     if( FD_UNLIKELY( (ulong)next_idx >= txn_max ) )
     139           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_one sibling_next_idx=%u txn_max=%lu)", next_idx, txn_max ));
     140           0 :     cache->txn.pool[ next_idx ].sibling_prev_idx = prev_idx;
     141           0 :   }
     142         123 :   if( prev_idx!=UINT_MAX ) {
     143           9 :     if( FD_UNLIKELY( (ulong)prev_idx >= txn_max ) )
     144           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_one sibling_prev_idx=%u txn_max=%lu)", prev_idx, txn_max ));
     145           9 :     cache->txn.pool[ prev_idx ].sibling_next_idx = next_idx;
     146           9 :   }
     147         123 :   if( txn->parent_idx!=UINT_MAX ) {
     148          57 :     if( FD_UNLIKELY( (ulong)txn->parent_idx >= txn_max ) )
     149           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_one parent_idx=%u txn_max=%lu)", txn->parent_idx, txn_max ));
     150          57 :     fd_progcache_txn_t * parent = &cache->txn.pool[ txn->parent_idx ];
     151          57 :     if( parent->child_head_idx==self_idx ) parent->child_head_idx = next_idx;
     152          57 :     if( parent->child_tail_idx==self_idx ) parent->child_tail_idx = prev_idx;
     153          66 :   } else {
     154          66 :     if( cache->shmem->txn.child_head_idx==self_idx ) cache->shmem->txn.child_head_idx = next_idx;
     155          66 :     if( cache->shmem->txn.child_tail_idx==self_idx ) cache->shmem->txn.child_tail_idx = prev_idx;
     156          66 :   }
     157             : 
     158             :   /* Remove transaction from index */
     159             : 
     160         123 :   if( FD_UNLIKELY( !fd_prog_txnm_ele_remove( cache->txn.map, &txn->xid, NULL, cache->txn.pool ) ) ) {
     161           0 :     FD_LOG_CRIT(( "fd_progcache_cancel failed: fd_prog_txnm_ele_remove(%lu) failed", txn->xid ));
     162           0 :   }
     163             : 
     164             :   /* Free transaction object */
     165             : 
     166         123 :   fd_rwlock_unwrite( &txn->lock );
     167         123 :   fd_prog_txnp_ele_release( cache->txn.pool, txn );
     168         123 : }
     169             : 
     170             : /* Cancels txn and all children */
     171             : 
     172             : static void
     173             : fd_progcache_cancel_tree( fd_progcache_join_t * cache,
     174         123 :                           fd_progcache_txn_t *  txn ) {
     175         123 :   ulong txn_max = fd_prog_txnp_max( cache->txn.pool );
     176         129 :   for(;;) {
     177         129 :     uint child_idx = txn->child_head_idx;
     178         129 :     if( child_idx==UINT_MAX ) break;
     179           6 :     if( FD_UNLIKELY( (ulong)child_idx >= txn_max ) )
     180           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_tree child_idx=%u txn_max=%lu)", child_idx, txn_max ));
     181           6 :     fd_progcache_txn_t * child = &cache->txn.pool[ child_idx ];
     182           6 :     fd_progcache_cancel_tree( cache, child );
     183           6 :   }
     184         123 :   fd_progcache_cancel_one( cache, txn );
     185         123 : }
     186             : 
     187             : /* Cancels all left/right siblings */
     188             : 
     189             : static void
     190             : fd_progcache_cancel_prev_list( fd_progcache_join_t * cache,
     191          36 :                                fd_progcache_txn_t *  txn ) {
     192          36 :   ulong txn_max = fd_prog_txnp_max( cache->txn.pool );
     193          36 :   uint cur_idx = txn->sibling_prev_idx;
     194          36 :   while( cur_idx!=UINT_MAX ) {
     195           0 :     if( FD_UNLIKELY( (ulong)cur_idx >= txn_max ) )
     196           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_prev_list txn_idx=%u txn_max=%lu)", cur_idx, txn_max ));
     197           0 :     fd_progcache_txn_t * sibling = &cache->txn.pool[ cur_idx ];
     198           0 :     uint next = sibling->sibling_prev_idx;
     199           0 :     fd_progcache_cancel_tree( cache, sibling );
     200           0 :     cur_idx = next;
     201           0 :   }
     202          36 : }
     203             : 
     204             : static void
     205             : fd_progcache_cancel_next_list( fd_progcache_join_t * cache,
     206          36 :                                fd_progcache_txn_t *  txn ) {
     207          36 :   ulong txn_max = fd_prog_txnp_max( cache->txn.pool );
     208          36 :   uint cur_idx = txn->sibling_next_idx;
     209          36 :   while( cur_idx!=UINT_MAX ) {
     210           0 :     if( FD_UNLIKELY( (ulong)cur_idx >= txn_max ) )
     211           0 :       FD_LOG_CRIT(( "progcache: corruption detected (cancel_next_list txn_idx=%u txn_max=%lu)", cur_idx, txn_max ));
     212           0 :     fd_progcache_txn_t * sibling = &cache->txn.pool[ cur_idx ];
     213           0 :     uint next = sibling->sibling_next_idx;
     214           0 :     fd_progcache_cancel_tree( cache, sibling );
     215           0 :     cur_idx = next;
     216           0 :   }
     217          36 : }
     218             : 
     219             : /* fd_progcache_txn_publish_one merges an in-prep transaction whose
     220             :    parent is the last published, into the parent. */
     221             : 
     222             : static void
     223             : fd_progcache_txn_publish_one( fd_progcache_join_t * cache,
     224          36 :                               fd_progcache_txn_t *  txn ) {
     225             : 
     226             :   /* Phase 1: Mark transaction as "last published" */
     227             : 
     228          36 :   fd_progcache_fork_id_t const fork_id = txn->xid;
     229          36 :   if( FD_UNLIKELY( txn->parent_idx!=UINT_MAX ) ) {
     230           0 :     FD_LOG_CRIT(( "fd_progcache_publish failed: txn with fork_id %lu is not a child of the last published txn", fork_id ));
     231           0 :   }
     232          36 :   fd_racesan_hook( "prog_publish_one:pre_xid_store" );
     233          36 :   __atomic_store_n( &cache->shmem->txn.root, fork_id, memory_order_release );
     234             : 
     235             :   /* Phase 2: Drain inserters from transaction */
     236             : 
     237          36 :   fd_rwlock_write( &txn->lock );
     238             : 
     239             :   /* Phase 3: Detach records */
     240             : 
     241          36 :   ulong rec_max = fd_prog_recp_ele_max( cache->rec.pool );
     242          42 :   for( uint idx = txn->rec_head_idx; idx!=UINT_MAX; idx = cache->rec.pool->ele[ idx ].next_idx ) {
     243           6 :     if( FD_UNLIKELY( (ulong)idx >= rec_max ) )
     244           0 :       FD_LOG_CRIT(( "progcache: corruption detected (publish_one rec_idx=%u rec_max=%lu)", idx, rec_max ));
     245           6 :     atomic_store_explicit( &cache->rec.pool->ele[ idx ].txn_idx, UINT_MAX, memory_order_release );
     246           6 :     fd_progcache_admin_metrics_g.root_cnt++;
     247           6 :   }
     248             : 
     249          36 :   txn->rec_head_idx = UINT_MAX;
     250          36 :   txn->rec_tail_idx = UINT_MAX;
     251             : 
     252             :   /* Phase 4: Remove transaction from fork graph */
     253             : 
     254          36 :   { /* Adjust the parent pointers of the children to point to "last published" */
     255          36 :     ulong txn_max = fd_prog_txnp_max( cache->txn.pool );
     256          36 :     ulong child_idx = txn->child_head_idx;
     257          42 :     while( child_idx!=UINT_MAX ) {
     258           6 :       if( FD_UNLIKELY( child_idx >= txn_max ) )
     259           0 :         FD_LOG_CRIT(( "progcache: corruption detected (publish_one child_idx=%lu txn_max=%lu)", child_idx, txn_max ));
     260           6 :       cache->txn.pool[ child_idx ].parent_idx = UINT_MAX;
     261           6 :       child_idx = cache->txn.pool[ child_idx ].sibling_next_idx;
     262           6 :     }
     263          36 :   }
     264             : 
     265             :   /* Phase 5: Remove transaction from index */
     266             : 
     267          36 :   if( FD_UNLIKELY( fd_prog_txnm_idx_remove( cache->txn.map, &txn->xid, ULONG_MAX, cache->txn.pool )==ULONG_MAX ) ) {
     268           0 :     FD_LOG_CRIT(( "fd_progcache_publish failed: fd_prog_txnm_idx_remove(%lu) failed", txn->xid ));
     269           0 :   }
     270             : 
     271             :   /* Phase 6: Free transaction object */
     272             : 
     273          36 :   fd_rwlock_unwrite( &txn->lock );
     274          36 :   txn->parent_idx       = UINT_MAX;
     275          36 :   txn->sibling_prev_idx = UINT_MAX;
     276          36 :   txn->sibling_next_idx = UINT_MAX;
     277          36 :   txn->child_head_idx   = UINT_MAX;
     278          36 :   txn->child_tail_idx   = UINT_MAX;
     279          36 :   fd_prog_txnp_ele_release( cache->txn.pool, txn );
     280          36 : }
     281             : 
     282             : void
     283             : fd_progcache_advance_root( fd_progcache_join_t *  cache,
     284          36 :                            fd_progcache_fork_id_t fork_id ) {
     285          36 :   if( FD_UNLIKELY( !cache ) ) FD_LOG_CRIT(( "invalid arguments" ));
     286             : 
     287             :   /* Detach records from txns without acquiring record locks */
     288             : 
     289          36 :   fd_rwlock_write( &cache->shmem->txn.rwlock );
     290             : 
     291          36 :   ulong txn_max = fd_prog_txnp_max( cache->txn.pool );
     292          36 :   uint txn_idx = (uint)fd_prog_txnm_idx_query( cache->txn.map, &fork_id, UINT_MAX, cache->txn.pool );
     293          36 :   if( FD_UNLIKELY( txn_idx==UINT_MAX ) ) {
     294           0 :     FD_LOG_CRIT(( "fd_progcache_advance_root failed: invalid fork_id %lu", fork_id ));
     295           0 :   }
     296          36 :   if( FD_UNLIKELY( (ulong)txn_idx >= txn_max ) )
     297           0 :     FD_LOG_CRIT(( "progcache: corruption detected (advance_root txn_idx=%u txn_max=%lu)", txn_idx, txn_max ));
     298          36 :   fd_progcache_txn_t * txn = &cache->txn.pool[ txn_idx ];
     299          36 :   if( FD_UNLIKELY( txn->parent_idx!=UINT_MAX ) ) {
     300           0 :     FD_LOG_CRIT(( "fd_progcache_advance_root: parent of txn %lu is not root", fork_id ));
     301           0 :   }
     302             : 
     303          36 :   fd_progcache_cancel_prev_list( cache, txn );
     304          36 :   fd_progcache_cancel_next_list( cache, txn );
     305             : 
     306          36 :   txn->sibling_prev_idx = UINT_MAX;
     307          36 :   txn->sibling_next_idx = UINT_MAX;
     308          36 :   cache->shmem->txn.child_head_idx = txn->child_head_idx;
     309          36 :   cache->shmem->txn.child_tail_idx = txn->child_tail_idx;
     310             : 
     311          36 :   fd_progcache_txn_publish_one( cache, txn );
     312             : 
     313          36 :   fd_rwlock_unwrite( &cache->shmem->txn.rwlock );
     314             : 
     315          36 :   fd_prog_reclaim_work( cache );
     316          36 : }
     317             : 
     318             : void
     319             : fd_progcache_cancel_fork( fd_progcache_join_t *  cache,
     320         117 :                           fd_progcache_fork_id_t fork_id ) {
     321         117 :   if( FD_UNLIKELY( !cache ) ) {
     322           0 :     FD_LOG_CRIT(( "invalid arguments" ));
     323           0 :   }
     324             : 
     325         117 :   fd_rwlock_write( &cache->shmem->txn.rwlock );
     326         117 :   fd_progcache_txn_t * txn = fd_prog_txnm_ele_query( cache->txn.map, &fork_id, NULL, cache->txn.pool );
     327         117 :   if( FD_UNLIKELY( !txn ) ) {
     328           0 :     FD_LOG_CRIT(( "fd_progcache_cancel failed: invalid fork_id %lu", fork_id ));
     329           0 :   }
     330         117 :   fd_progcache_cancel_tree( cache, txn );
     331         117 :   fd_rwlock_unwrite( &cache->shmem->txn.rwlock );
     332         117 :   fd_prog_reclaim_work( cache );
     333         117 : }
     334             : 
     335             : /* reset_rec_map frees all records in a progcache instance. */
     336             : 
     337             : static void
     338        3588 : reset_rec_map( fd_progcache_join_t * cache ) {
     339        3588 :   fd_progcache_rec_t * rec0 = cache->rec.pool->ele;
     340        3588 :   ulong chain_cnt = fd_prog_recm_chain_cnt( cache->rec.map );
     341      462852 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     342      459264 :     for(
     343      459264 :         fd_prog_recm_iter_t iter = fd_prog_recm_iter( cache->rec.map, chain_idx );
     344      459264 :         !fd_prog_recm_iter_done( iter );
     345      459264 :     ) {
     346           0 :       fd_progcache_rec_t * rec = fd_prog_recm_iter_ele( iter );
     347           0 :       ulong next = fd_prog_recm_private_idx( rec->map_next );
     348             : 
     349           0 :       if( rec->exists ) {
     350           0 :         fd_prog_recm_query_t rec_query[1];
     351           0 :         int err = fd_prog_recm_remove( cache->rec.map, &rec->pair, NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     352           0 :         if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_prog_recm_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
     353           0 :         fd_progcache_val_free( rec, cache );
     354           0 :       }
     355             : 
     356           0 :       rec->exists = 0;
     357           0 :       fd_prog_clock_remove( cache->clock.bits, (ulong)( rec-rec0 ) );
     358           0 :       fd_prog_recp_release( cache->rec.pool, rec );
     359           0 :       iter.ele_idx = next;
     360           0 :     }
     361      459264 :   }
     362        3588 : }
     363             : 
     364             : /* clear_txn_list does a depth-first traversal of the txn tree.
     365             :    Removes all txns. */
     366             : 
     367             : static void
     368             : clear_txn_list( fd_progcache_join_t * join,
     369        7182 :                 uint                  txn_head_idx ) {
     370        7182 :   ulong txn_max = fd_prog_txnp_max( join->txn.pool );
     371       10776 :   for( uint idx = txn_head_idx; idx!=UINT_MAX; ) {
     372        3594 :     if( FD_UNLIKELY( (ulong)idx >= txn_max ) )
     373           0 :       FD_LOG_CRIT(( "progcache: corruption detected (clear_txn_list txn_idx=%u txn_max=%lu)", idx, txn_max ));
     374        3594 :     fd_progcache_txn_t * txn = &join->txn.pool[ idx ];
     375        3594 :     uint next_idx  = txn->sibling_next_idx;
     376        3594 :     uint child_idx = txn->child_head_idx;
     377        3594 :     txn->rec_head_idx     = UINT_MAX;
     378        3594 :     txn->rec_tail_idx     = UINT_MAX;
     379        3594 :     txn->child_head_idx   = UINT_MAX;
     380        3594 :     txn->child_tail_idx   = UINT_MAX;
     381        3594 :     txn->parent_idx       = UINT_MAX;
     382        3594 :     txn->sibling_prev_idx = UINT_MAX;
     383        3594 :     txn->sibling_next_idx = UINT_MAX;
     384        3594 :     clear_txn_list( join, child_idx );
     385        3594 :     if( FD_UNLIKELY( !fd_prog_txnm_ele_remove( join->txn.map, &txn->xid, NULL, join->txn.pool ) ) ) FD_LOG_CRIT(( "fd_prog_txnm_ele_remove failed" ));
     386        3594 :     fd_prog_txnp_ele_release( join->txn.pool, txn );
     387        3594 :     idx = next_idx;
     388        3594 :   }
     389        7182 : }
     390             : 
     391             : void
     392        3588 : fd_progcache_reset( fd_progcache_join_t * cache ) {
     393        3588 :   clear_txn_list( cache, cache->shmem->txn.child_head_idx );
     394        3588 :   cache->shmem->txn.child_head_idx = UINT_MAX;
     395        3588 :   cache->shmem->txn.child_tail_idx = UINT_MAX;
     396        3588 :   reset_rec_map( cache );
     397        3588 :   cache->shmem->txn.root = fd_progcache_fork_id_initial();
     398        3588 :   cache->shmem->txn.seq  = fd_progcache_fork_id_initial();
     399        3588 : }
     400             : 
     401             : static int
     402             : fd_progcache_verify_siblings( fd_progcache_txn_t * pool,
     403             :                               ulong                txn_max,
     404             :                               uint                 head_idx,
     405             :                               uint                 tail_idx,
     406             :                               uint                 expected_parent_idx,
     407             :                               uint *               stack,
     408          87 :                               ulong *              stack_top ) {
     409             : 
     410         210 : # define TEST(c) do {                                                    \
     411         210 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return -1; } \
     412         210 :   } while(0)
     413             : 
     414          87 :   TEST( (head_idx==UINT_MAX)==(tail_idx==UINT_MAX) );
     415             : 
     416          87 :   uint last_idx = UINT_MAX;
     417          96 :   for( uint idx = head_idx; idx!=UINT_MAX; ) {
     418           9 :     TEST( idx<txn_max );
     419           9 :     fd_progcache_txn_t * child = &pool[ idx ];
     420           9 :     TEST( !child->tag );
     421           9 :     TEST( child->parent_idx==expected_parent_idx );
     422           9 :     child->tag = 1;
     423           9 :     TEST( *stack_top<FD_PROGCACHE_DEPTH_MAX );
     424           9 :     stack[ (*stack_top)++ ] = idx;
     425           9 :     last_idx = idx;
     426           9 :     uint next_idx = child->sibling_next_idx;
     427           9 :     if( next_idx!=UINT_MAX ) {
     428           0 :       TEST( next_idx<txn_max );
     429           0 :       TEST( pool[ next_idx ].sibling_prev_idx==idx );
     430           0 :     }
     431           9 :     idx = next_idx;
     432           9 :   }
     433          87 :   TEST( last_idx==tail_idx );
     434             : 
     435          87 : # undef TEST
     436             : 
     437          87 :   return 0;
     438          87 : }
     439             : 
     440             : int
     441          78 : fd_progcache_verify( fd_progcache_join_t * join ) {
     442             : 
     443         690 : # define TEST(c) do {                                                    \
     444         690 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return -1; } \
     445         690 :   } while(0)
     446             : 
     447          78 :   TEST( join );
     448             : 
     449          78 :   fd_progcache_shmem_t * shmem = join->shmem;
     450          78 :   TEST( shmem );
     451          78 :   TEST( shmem->magic==FD_PROGCACHE_SHMEM_MAGIC );
     452          78 :   TEST( shmem->wksp_tag );
     453             : 
     454          78 :   TEST( !fd_prog_recp_verify( join->rec.pool ) );
     455          78 :   TEST( !fd_prog_recm_verify( join->rec.map ) );
     456             : 
     457          78 :   ulong rec_max = fd_prog_recp_ele_max( join->rec.pool );
     458          78 :   fd_progcache_rec_t * rec0 = join->rec.pool->ele;
     459             : 
     460          78 :   ulong txn_max = fd_prog_txnp_max( join->txn.pool );
     461          78 :   TEST( !fd_prog_txnm_verify( join->txn.map, txn_max, join->txn.pool ) );
     462             : 
     463        1326 :   for( ulong i=0UL; i<txn_max; i++ ) join->txn.pool[ i ].tag = 0;
     464             : 
     465          78 :   uint  stack[ FD_PROGCACHE_DEPTH_MAX ];
     466          78 :   ulong stack_top = 0UL;
     467             : 
     468          78 :   TEST( !fd_progcache_verify_siblings( join->txn.pool, txn_max,
     469          78 :       shmem->txn.child_head_idx, shmem->txn.child_tail_idx,
     470          78 :       UINT_MAX, stack, &stack_top ) );
     471             : 
     472          87 :   while( stack_top ) {
     473           9 :     uint txn_idx = stack[ --stack_top ];
     474           9 :     fd_progcache_txn_t * txn = &join->txn.pool[ txn_idx ];
     475           9 :     TEST( !fd_progcache_verify_siblings( join->txn.pool, txn_max,
     476           9 :         txn->child_head_idx, txn->child_tail_idx,
     477           9 :         txn_idx, stack, &stack_top ) );
     478           9 :   }
     479             : 
     480        1326 :   for( ulong i=0UL; i<txn_max; i++ ) {
     481        1248 :     if( !join->txn.pool[ i ].tag ) continue;
     482           9 :     fd_progcache_txn_t * txn = &join->txn.pool[ i ];
     483             : 
     484           9 :     TEST( (txn->rec_head_idx==UINT_MAX)==(txn->rec_tail_idx==UINT_MAX) );
     485             : 
     486           9 :     ulong rec_cnt = 0UL;
     487           9 :     uint  prev    = UINT_MAX;
     488          12 :     for( uint idx = txn->rec_head_idx; idx!=UINT_MAX; ) {
     489           3 :       TEST( idx<rec_max );
     490           3 :       TEST( rec_cnt<rec_max ); /* cycle detection */
     491           3 :       fd_progcache_rec_t * rec = &rec0[ idx ];
     492           3 :       TEST( rec->prev_idx==prev );
     493           3 :       TEST( rec->exists );
     494           3 :       prev = idx;
     495           3 :       idx  = rec->next_idx;
     496           3 :       rec_cnt++;
     497           3 :     }
     498           9 :     TEST( prev==txn->rec_tail_idx );
     499           9 :   }
     500             : 
     501          78 :   ulong chain_cnt = fd_prog_recm_chain_cnt( join->rec.map );
     502        1326 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     503        1248 :     for(
     504        1248 :         fd_prog_recm_iter_t iter = fd_prog_recm_iter( join->rec.map, chain_idx );
     505        1257 :         !fd_prog_recm_iter_done( iter );
     506        1248 :         iter = fd_prog_recm_iter_next( iter )
     507        1248 :     ) {
     508           9 :       fd_progcache_rec_t * rec = fd_prog_recm_iter_ele( iter );
     509           9 :       TEST( rec->exists );
     510             : 
     511             :       /* Verify clock exists bit is set for mapped records */
     512           9 :       ulong rec_idx = (ulong)( rec - rec0 );
     513           9 :       TEST( rec_idx<rec_max );
     514           9 :       atomic_ulong * slot_p = fd_prog_cbits_slot( join->clock.bits, rec_idx );
     515           9 :       ulong slot_val = atomic_load_explicit( slot_p, memory_order_relaxed );
     516           9 :       TEST( fd_ulong_extract_bit( slot_val, fd_prog_exists_bit( rec_idx ) ) );
     517           9 :     }
     518        1248 :   }
     519             : 
     520          78 :   {
     521          78 :     ulong reclaim_cnt = 0UL;
     522          78 :     for( uint idx = join->rec.reclaim_head; idx!=UINT_MAX; ) {
     523           0 :       TEST( idx<rec_max );
     524           0 :       TEST( reclaim_cnt<rec_max ); /* cycle detection */
     525           0 :       idx = rec0[ idx ].reclaim_next;
     526           0 :       reclaim_cnt++;
     527           0 :     }
     528          78 :   }
     529             : 
     530          78 : # undef TEST
     531             : 
     532          78 :   return 0;
     533          78 : }

Generated by: LCOV version 1.14