LCOV - code coverage report
Current view: top level - funk - fd_funk_txn.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 389 513 75.8 %
Date: 2025-07-15 04:56:17 Functions: 23 31 74.2 %

          Line data    Source code
       1             : #include "fd_funk.h"
       2             : 
       3             : /* Provide the actual transaction map implementation */
       4             : 
       5             : #define POOL_NAME          fd_funk_txn_pool
       6     2111415 : #define POOL_ELE_T         fd_funk_txn_t
       7             : #define POOL_IDX_T         uint
       8     5260746 : #define POOL_NEXT          map_next
       9             : #define POOL_IMPL_STYLE    2
      10             : #include "../util/tmpl/fd_pool_para.c"
      11             : 
      12             : #define MAP_NAME              fd_funk_txn_map
      13   387426618 : #define MAP_ELE_T             fd_funk_txn_t
      14           0 : #define MAP_KEY_T             fd_funk_txn_xid_t
      15     1055640 : #define MAP_KEY               xid
      16             : #define MAP_KEY_EQ(k0,k1)     fd_funk_txn_xid_eq((k0),(k1))
      17             : #define MAP_KEY_HASH(k0,seed) fd_funk_txn_xid_hash((k0),(seed))
      18   164877203 : #define MAP_NEXT              map_next
      19             : #define MAP_HASH              map_hash
      20          33 : #define MAP_MAGIC             (0xf173da2ce7172db0UL) /* Firedancer txn db version 0 */
      21             : #define MAP_IMPL_STYLE        2
      22             : #include "../util/tmpl/fd_map_chain_para.c"
      23             : 
      24             : /* TODO: remove this lock */
      25             : #include "../flamenco/fd_rwlock.h"
      26             : static fd_rwlock_t funk_txn_lock[ 1 ] = {0};
      27             : 
      28             : void
      29           0 : fd_funk_txn_start_read( fd_funk_t * funk FD_PARAM_UNUSED ) {
      30           0 :   fd_rwlock_read( funk_txn_lock );
      31           0 : }
      32             : 
      33             : void
      34           0 : fd_funk_txn_end_read( fd_funk_t * funk FD_PARAM_UNUSED ) {
      35           0 :   fd_rwlock_unread( funk_txn_lock );
      36           0 : }
      37             : 
      38             : void
      39          15 : fd_funk_txn_start_write( fd_funk_t * funk FD_PARAM_UNUSED ) {
      40          15 :   fd_rwlock_write( funk_txn_lock );
      41          15 : }
      42             : 
      43             : void
      44          15 : fd_funk_txn_end_write( fd_funk_t * funk FD_PARAM_UNUSED ) {
      45          15 :   fd_rwlock_unwrite( funk_txn_lock );
      46          15 : }
      47             : 
      48             : fd_funk_txn_t *
      49             : fd_funk_txn_prepare( fd_funk_t *               funk,
      50             :                      fd_funk_txn_t *           parent,
      51             :                      fd_funk_txn_xid_t const * xid,
      52     1317777 :                      int                       verbose ) {
      53             : 
      54             : #ifdef FD_FUNK_HANDHOLDING
      55             :   if( FD_UNLIKELY( !funk ) ) {
      56             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL funk" ));
      57             :     return NULL;
      58             :   }
      59             :   if( FD_UNLIKELY( !xid ) ) {
      60             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL xid" ));
      61             :     return NULL;
      62             :   }
      63             :   if( FD_UNLIKELY( parent && !fd_funk_txn_valid( funk, parent ) ) ) {
      64             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "bad txn" ));
      65             :     return NULL;
      66             :   }
      67             :   if( FD_UNLIKELY( fd_funk_txn_xid_eq_root( xid ) ) ) {
      68             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "xid is the root" ));
      69             :     return NULL;
      70             :   }
      71             :   if( FD_UNLIKELY( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) ) {
      72             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "xid is the last published" ));
      73             :     return NULL;
      74             :   }
      75             : #endif
      76             : 
      77     1317777 :   fd_funk_txn_map_query_t query[1];
      78     1317777 :   if( FD_UNLIKELY( fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 ) != FD_MAP_ERR_KEY ) ) {
      79      262125 :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "xid in use" ));
      80      262125 :     return NULL;
      81      262125 :   }
      82             : 
      83     1055652 :   ulong  parent_idx;
      84     1055652 :   uint * _child_head_cidx;
      85     1055652 :   uint * _child_tail_cidx;
      86             : 
      87     1055652 :   if( FD_LIKELY( !parent ) ) { /* opt for incr pub */
      88             : 
      89      445418 :     parent_idx = FD_FUNK_TXN_IDX_NULL;
      90             : 
      91      445418 :     _child_head_cidx = &funk->shmem->child_head_cidx;
      92      445418 :     _child_tail_cidx = &funk->shmem->child_tail_cidx;
      93             : 
      94      610234 :   } else {
      95             : 
      96      610234 :     parent_idx = (ulong)(parent - funk->txn_pool->ele);
      97             : 
      98      610234 :     _child_head_cidx = &parent->child_head_cidx;
      99      610234 :     _child_tail_cidx = &parent->child_tail_cidx;
     100             : 
     101      610234 :   }
     102             : 
     103             :   /* Get a new transaction from the map */
     104             : 
     105     1055652 :   fd_funk_txn_t * txn = fd_funk_txn_pool_acquire( funk->txn_pool, NULL, 1, NULL );
     106     1055652 :   if( txn == NULL ) {
     107          12 :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "transaction pool is exhuasted" ));
     108          12 :     return NULL;
     109          12 :   }
     110     1055640 :   fd_funk_txn_xid_copy( &txn->xid, xid );
     111     1055640 :   ulong txn_idx = (ulong)(txn - funk->txn_pool->ele);
     112             : 
     113             :   /* Join the family */
     114             : 
     115     1055640 :   ulong sibling_prev_idx = fd_funk_txn_idx( *_child_tail_cidx );
     116             : 
     117     1055640 :   int first_born = fd_funk_txn_idx_is_null( sibling_prev_idx );
     118             : 
     119     1055640 :   txn->parent_cidx       = fd_funk_txn_cidx( parent_idx           );
     120     1055640 :   txn->child_head_cidx   = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     121     1055640 :   txn->child_tail_cidx   = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     122     1055640 :   txn->sibling_prev_cidx = fd_funk_txn_cidx( sibling_prev_idx     );
     123     1055640 :   txn->sibling_next_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     124     1055640 :   txn->stack_cidx        = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     125     1055640 :   txn->tag               = 0UL;
     126             : 
     127     1055640 :   txn->lock = 0;
     128     1055640 :   txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
     129     1055640 :   txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     130             : 
     131             :   /* TODO: consider branchless impl */
     132     1055640 :   if( FD_LIKELY( first_born ) ) *_child_head_cidx                = fd_funk_txn_cidx( txn_idx ); /* opt for non-compete */
     133      537283 :   else funk->txn_pool->ele[ sibling_prev_idx ].sibling_next_cidx = fd_funk_txn_cidx( txn_idx );
     134             : 
     135     1055640 :   *_child_tail_cidx = fd_funk_txn_cidx( txn_idx );
     136             : 
     137     1055640 :   fd_funk_txn_map_insert( funk->txn_map, txn, FD_MAP_FLAG_BLOCKING );
     138             : 
     139     1055640 :   return txn;
     140     1055652 : }
     141             : 
     142             : /* fd_funk_txn_cancel_childless cancels a transaction that is known
     143             :    to be childless.  Callers have already validated our input arguments.
     144             :    Assumes that cancelling in the app can't fail but that could be
     145             :    straightforward to support by giving this an error and plumbing
     146             :    through to abort the overall cancel operation when it hits a error. */
     147             : 
     148             : static void
     149             : fd_funk_txn_cancel_childless( fd_funk_t * funk,
     150      837970 :                               ulong       txn_idx ) {
     151             : 
     152             :   /* Remove all records used by this transaction.  Note that we don't
     153             :      need to bother doing all the individual removal operations as we
     154             :      are removing the whole list.  We do reset the record transaction
     155             :      idx with NULL though we can detect cycles as soon as possible
     156             :      and abort. */
     157             : 
     158      837970 :   fd_wksp_t *          wksp     = funk->wksp;
     159      837970 :   fd_alloc_t *         alloc    = funk->alloc;
     160      837970 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     161      837970 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     162      837970 :   ulong                rec_max  = fd_funk_rec_pool_ele_max( rec_pool );
     163      837970 :   fd_funk_txn_map_t *  txn_map  = funk->txn_map;
     164      837970 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     165             : 
     166      837970 :   fd_funk_txn_t * txn = &txn_pool->ele[ txn_idx ];
     167      837970 :   uint rec_idx = txn->rec_head_idx;
     168    20036546 :   while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     169             : 
     170    19198576 :     if( FD_UNLIKELY( rec_idx>=rec_max ) ) FD_LOG_CRIT(( "memory corruption detected (bad idx)" ));
     171    19198576 :     if( FD_UNLIKELY( fd_funk_txn_idx( rec_pool->ele[ rec_idx ].txn_cidx )!=txn_idx ) )
     172           0 :       FD_LOG_CRIT(( "memory corruption detected (cycle or bad idx)" ));
     173             : 
     174    19198576 :     fd_funk_rec_t * rec = &rec_pool->ele[ rec_idx ];
     175    19198576 :     uint next_idx = rec->next_idx;
     176    19198576 :     rec->txn_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     177             : 
     178    19198576 :     for(;;) {
     179    19198576 :       fd_funk_rec_map_query_t rec_query[1];
     180    19198576 :       int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     181    19198576 :       if( FD_UNLIKELY( err == FD_MAP_ERR_AGAIN ) ) continue;
     182    19198576 :       if( err == FD_MAP_ERR_KEY ) break;
     183    19198576 :       if( FD_UNLIKELY( err != FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "map corruption" ));
     184    19198576 :       if( rec != fd_funk_rec_map_query_ele( rec_query ) ) break;
     185    19198576 :       fd_funk_val_flush( rec, alloc, wksp );
     186    19198576 :       fd_funk_rec_pool_release( rec_pool, rec, 1 );
     187    19198576 :       break;
     188    19198576 :     }
     189             : 
     190    19198576 :     rec_idx = next_idx;
     191    19198576 :   }
     192             : 
     193             :   /* Leave the family */
     194             : 
     195      837970 :   ulong sibling_prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
     196      837970 :   ulong sibling_next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
     197             : 
     198             :   /* TODO: Consider branchless impl */
     199             : 
     200      837970 :   if( FD_LIKELY( fd_funk_txn_idx_is_null( sibling_prev_idx ) ) ) { /* opt for non-compete */
     201      407273 :     ulong parent_idx = fd_funk_txn_idx( txn_pool->ele[ txn_idx ].parent_cidx );
     202      407273 :     if( FD_LIKELY( fd_funk_txn_idx_is_null( parent_idx ) ) ) { /* No older sib and is a funk child, opt for incr pub */
     203      136749 :       funk->shmem->child_head_cidx = fd_funk_txn_cidx( sibling_next_idx );
     204      270524 :     } else { /* No older sib and has parent */
     205      270524 :       txn_pool->ele[ parent_idx ].child_head_cidx = fd_funk_txn_cidx( sibling_next_idx );
     206      270524 :     }
     207      430697 :   } else { /* Has older sib */
     208      430697 :     txn_pool->ele[ sibling_prev_idx ].sibling_next_cidx = fd_funk_txn_cidx( sibling_next_idx );
     209      430697 :   }
     210             : 
     211      837970 :   if( FD_LIKELY( fd_funk_txn_idx_is_null( sibling_next_idx ) ) ) { /* opt for non-compete */
     212      660533 :     ulong parent_idx = fd_funk_txn_idx( txn_pool->ele[ txn_idx ].parent_cidx );
     213      660533 :     if( FD_LIKELY( fd_funk_txn_idx_is_null( parent_idx ) ) ) { /* No younger sib and is a funk child, opt for incr pub */
     214      262899 :       funk->shmem->child_tail_cidx = fd_funk_txn_cidx( sibling_prev_idx );
     215      397634 :     } else { /* No younger sib and has parent */
     216      397634 :       txn_pool->ele[ parent_idx ].child_tail_cidx = fd_funk_txn_cidx( sibling_prev_idx );
     217      397634 :     }
     218      660533 :   } else { /* Has younger sib */
     219      177437 :     txn_pool->ele[ sibling_next_idx ].sibling_prev_cidx = fd_funk_txn_cidx( sibling_prev_idx );
     220      177437 :   }
     221             : 
     222      837970 :   fd_funk_txn_map_query_t query[1];
     223      837970 :   if( fd_funk_txn_map_remove( txn_map, fd_funk_txn_xid( txn ), NULL, query, FD_MAP_FLAG_BLOCKING ) == FD_MAP_SUCCESS ) {
     224      837970 :     fd_funk_txn_pool_release( txn_pool, txn, 1 );
     225      837970 :   }
     226      837970 : }
     227             : 
     228             : /* fd_funk_txn_cancel_family cancels a transaction and all its
     229             :    descendants in a tree-depth-first-ordered sense from youngest to
     230             :    oldest.  Callers have already validated our input arguments.  Returns
     231             :    the number of transactions canceled. */
     232             : 
     233             : static ulong
     234             : fd_funk_txn_cancel_family( fd_funk_t *  funk,
     235             :                            ulong        tag,
     236      475250 :                            ulong        txn_idx ) {
     237      475250 :   ulong cancel_cnt = 0UL;
     238             : 
     239      475250 :   ulong parent_stack_idx = FD_FUNK_TXN_IDX_NULL;
     240             : 
     241     1200690 :   for(;;) {
     242             : 
     243     1200690 :     fd_funk_txn_t * txn = &funk->txn_pool->ele[ txn_idx ];
     244     1200690 :     txn->tag = tag;
     245             : 
     246     1200690 :     ulong youngest_idx = fd_funk_txn_idx( txn->child_tail_cidx );
     247     1200690 :     if( FD_LIKELY( fd_funk_txn_idx_is_null( youngest_idx ) ) ) { /* txn is is childless, opt for incr pub */
     248             : 
     249      837970 :       fd_funk_txn_cancel_childless( funk, txn_idx ); /* If this can fail, return cancel_cnt here on fail */
     250      837970 :       cancel_cnt++;
     251             : 
     252      837970 :       txn_idx = parent_stack_idx;                                  /* Pop the parent stack */
     253      837970 :       if( FD_LIKELY( fd_funk_txn_idx_is_null( txn_idx ) ) ) break; /* If stack is empty, we are done, opt for incr pub */
     254      362720 :       parent_stack_idx = fd_funk_txn_idx( funk->txn_pool->ele[ txn_idx ].stack_cidx );
     255      362720 :       continue;
     256      837970 :     }
     257             : 
     258             :     /* txn has at least one child and the youngest is youngest_idx.  Tag
     259             :        the youngest child, push txn onto the parent stack and recurse
     260             :        into the youngest child. */
     261             : 
     262      362720 :     txn->stack_cidx = fd_funk_txn_cidx( parent_stack_idx );
     263      362720 :     parent_stack_idx = txn_idx;
     264             : 
     265      362720 :     txn_idx = youngest_idx;
     266      362720 :   }
     267             : 
     268      475250 :   return cancel_cnt;
     269      475250 : }
     270             : 
     271             : ulong
     272             : fd_funk_txn_cancel( fd_funk_t *     funk,
     273             :                     fd_funk_txn_t * txn,
     274      147150 :                     int             verbose ) {
     275             : 
     276             : #ifdef FD_FUNK_HANDHOLDING
     277             :   if( FD_UNLIKELY( !funk ) ) {
     278             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL funk" ));
     279             :     return 0UL;
     280             :   }
     281             :   if( FD_UNLIKELY( !fd_funk_txn_valid( funk, txn ) ) ) {
     282             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "bad txn" ));
     283             :     return 0UL;
     284             :   }
     285             : #else
     286      147150 :   (void)verbose;
     287      147150 : #endif
     288             : 
     289      147150 :   ulong txn_idx = (ulong)(txn - funk->txn_pool->ele);
     290      147150 :   return fd_funk_txn_cancel_family( funk, funk->shmem->cycle_tag++, txn_idx );
     291      147150 : }
     292             : 
     293             : /* fd_funk_txn_oldest_sibling returns the index of the oldest sibling
     294             :    in txn_idx's family.  Callers have already validated our input
     295             :    arguments.  The caller should validate the return index. */
     296             : 
     297             : static inline ulong
     298             : fd_funk_txn_oldest_sibling( fd_funk_t *  funk,
     299      215984 :                             ulong        txn_idx ) {
     300      215984 :   ulong parent_idx = fd_funk_txn_idx( funk->txn_pool->ele[ txn_idx ].parent_cidx );
     301             : 
     302      215984 :   if( FD_LIKELY( fd_funk_txn_idx_is_null( parent_idx ) ) ) return fd_funk_txn_idx( funk->shmem->child_head_cidx ); /* opt for incr pub */
     303             : 
     304        9432 :   return fd_funk_txn_idx( funk->txn_pool->ele[ parent_idx ].child_head_cidx );
     305      215984 : }
     306             : 
     307             : /* fd_funk_txn_cancel_sibling_list cancels siblings from sibling_idx down
     308             :    to the youngest sibling inclusive in the order from youngest to
     309             :    sibling_idx.  Callers have already validated our input arguments
     310             :    except sibling_idx.  Returns the number of cancelled transactions
     311             :    (should be at least 1).  If any sibling is skip_idx, it will be not
     312             :    be cancelled but still tagged as visited.  Passing
     313             :    FD_FUNK_TXN_IDX_NULL for skip_idx will cancel all siblings from
     314             :    sibling_idx to the youngest inclusive. */
     315             : 
     316             : static ulong
     317             : fd_funk_txn_cancel_sibling_list( fd_funk_t * funk,
     318             :                                     ulong          tag,
     319             :                                     ulong          sibling_idx,
     320      215984 :                                     ulong          skip_idx ) {
     321             : 
     322      215984 :   ulong cancel_stack_idx = FD_FUNK_TXN_IDX_NULL;
     323             : 
     324             :   /* Push siblings_idx and its younger siblings inclusive (skipping
     325             :      sibling skip_idx if encounter) onto the cancel stack from oldest to
     326             :      youngest (such that we cancel youngest to oldest). */
     327             : 
     328      544084 :   for(;;) {
     329             : 
     330             :     /* At this point, sibling_idx is a sibling we might want to add to
     331             :        the sibling stack.  Validate and tag it. */
     332             : 
     333      544084 :     fd_funk_txn_t * sibling = &funk->txn_pool->ele[ sibling_idx ];
     334      544084 :     sibling->tag = tag;
     335             : 
     336      544084 :     if( FD_UNLIKELY( sibling_idx!=skip_idx ) ) { /* Not skip_idx so push onto the cancel stack, opt for non-compete */
     337      328100 :       sibling->stack_cidx = fd_funk_txn_cidx( cancel_stack_idx );
     338      328100 :       cancel_stack_idx = sibling_idx;
     339      328100 :     }
     340             : 
     341      544084 :     ulong younger_idx = fd_funk_txn_idx( sibling->sibling_next_cidx );
     342      544084 :     if( FD_LIKELY( fd_funk_txn_idx_is_null( younger_idx ) ) ) break; /* opt for non-compete */
     343      328100 :     sibling_idx = younger_idx;
     344             : 
     345      328100 :   }
     346             : 
     347             :   /* Cancel all transactions and their descendants on the cancel stack */
     348             : 
     349      215984 :   ulong cancel_cnt = 0UL;
     350             : 
     351      544084 :   while( !fd_funk_txn_idx_is_null( cancel_stack_idx ) ) { /* TODO: peel first iter to make more predictable? */
     352      328100 :     ulong sibling_idx = cancel_stack_idx;
     353      328100 :     cancel_stack_idx  = fd_funk_txn_idx( funk->txn_pool->ele[ sibling_idx ].stack_cidx );
     354      328100 :     cancel_cnt += fd_funk_txn_cancel_family( funk, tag, sibling_idx );
     355      328100 :   }
     356             : 
     357      215984 :   return cancel_cnt;
     358      215984 : }
     359             : 
     360             : ulong
     361             : fd_funk_txn_cancel_siblings( fd_funk_t *     funk,
     362             :                              fd_funk_txn_t * txn,
     363           0 :                              int             verbose ) {
     364             : 
     365             : #ifdef FD_FUNK_HANDHOLDING
     366             :   if( FD_UNLIKELY( !funk ) ) {
     367             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL funk" ));
     368             :     return 0UL;
     369             :   }
     370             :   if( FD_UNLIKELY( !fd_funk_txn_valid( funk, txn ) ) ) {
     371             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "bad txn" ));
     372             :     return 0UL;
     373             :   }
     374             : #else
     375           0 :   (void)verbose;
     376           0 : #endif
     377             : 
     378           0 :   ulong txn_idx = (ulong)(txn - funk->txn_pool->ele);
     379             : 
     380           0 :   ulong oldest_idx = fd_funk_txn_oldest_sibling( funk, txn_idx );
     381             : 
     382           0 :   return fd_funk_txn_cancel_sibling_list( funk, funk->shmem->cycle_tag++, oldest_idx, txn_idx );
     383           0 : }
     384             : 
     385             : ulong
     386             : fd_funk_txn_cancel_children( fd_funk_t *     funk,
     387             :                              fd_funk_txn_t * txn,
     388           0 :                              int             verbose ) {
     389             : 
     390             : #ifdef FD_FUNK_HANDHOLDING
     391             :   if( FD_UNLIKELY( !funk ) ) {
     392             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL funk" ));
     393             :     return 0UL;
     394             :   }
     395             :   if( FD_UNLIKELY( !fd_funk_txn_valid( funk, txn ) ) ) {
     396             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "bad txn" ));
     397             :     return 0UL;
     398             :   }
     399             : #else
     400           0 :   (void)verbose;
     401           0 : #endif
     402             : 
     403           0 :   ulong oldest_idx;
     404             : 
     405           0 :   if( FD_LIKELY( txn == NULL ) ) {
     406           0 :     oldest_idx = fd_funk_txn_idx( funk->shmem->child_head_cidx ); /* opt for non-compete */
     407           0 :   } else {
     408           0 :     oldest_idx = fd_funk_txn_idx( txn->child_head_cidx );
     409           0 :   }
     410             : 
     411           0 :   if( fd_funk_txn_idx_is_null( oldest_idx ) ) {
     412           0 :     return 0UL;
     413           0 :   }
     414             : 
     415           0 :   return fd_funk_txn_cancel_sibling_list( funk, funk->shmem->cycle_tag++, oldest_idx, FD_FUNK_TXN_IDX_NULL );
     416           0 : }
     417             : 
     418             : ulong
     419           0 : fd_funk_txn_cancel_root( fd_funk_t * funk ) {
     420           0 :   fd_funk_rec_map_t * rec_map = funk->rec_map;
     421           0 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     422           0 :   ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
     423           0 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     424           0 :     for(
     425           0 :         fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
     426           0 :         !fd_funk_rec_map_iter_done( iter );
     427           0 :         iter = fd_funk_rec_map_iter_next( iter )
     428           0 :     ) {
     429           0 :       for (;;) {
     430           0 :         fd_funk_rec_map_query_t rec_query[1];
     431           0 :         int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( iter.ele ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     432           0 :         if( FD_UNLIKELY( err == FD_MAP_ERR_AGAIN ) ) continue;
     433           0 :         if( err == FD_MAP_ERR_KEY ) break;
     434           0 :         if( FD_UNLIKELY( err != FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "map corruption" ));
     435           0 :         if( iter.ele != fd_funk_rec_map_query_ele( rec_query ) ) break;
     436           0 :         fd_funk_val_flush( fd_funk_rec_map_iter_ele( iter ), fd_funk_alloc( funk ), fd_funk_wksp( funk ) );
     437           0 :         fd_funk_rec_pool_release( rec_pool, fd_funk_rec_map_query_ele( rec_query ), 1 );
     438           0 :       }
     439           0 :     }
     440           0 :   }
     441             : 
     442           0 :   return 0UL;
     443           0 : }
     444             : 
     445             : /* Cancel all outstanding transactions */
     446             : 
     447             : ulong
     448             : fd_funk_txn_cancel_all( fd_funk_t *     funk,
     449           0 :                         int             verbose ) {
     450           0 :   return fd_funk_txn_cancel_children( funk, NULL, verbose );
     451           0 : }
     452             : 
     453             : /* fd_funk_txn_update applies the record updates in transaction txn_idx
     454             :    to another transaction or the parent transaction.  Callers have
     455             :    already validated our input arguments.
     456             : 
     457             :    On entry, the head/tail of the destination records are at
     458             :    *_dst_rec_head_idx / *_dst_rec_tail_idx.  All transactions on this
     459             :    list will have transaction id dst_xid and vice versa.  That is, this
     460             :    is the record list the last published transaction or txn_idx's
     461             :    in-prep parent transaction.
     462             : 
     463             :    On exit, the head/tail of the updated records is at
     464             :    *_dst_rec_head_idx / *_dst_rec_tail_idx.  As before, all transactions
     465             :    on this list will have transaction id dst_xid and vice versa.
     466             :    Transaction txn_idx will have an _empty_ record list.
     467             : 
     468             :    Updates in the transaction txn_idx are processed from oldest to
     469             :    youngest.  If an update erases an existing record in dest, the record
     470             :    to erase is removed from the destination records without perturbing
     471             :    the order of remaining destination records.  If an update is to
     472             :    update an existing record, the destination record value is updated
     473             :    and the order of the destination records is unchanged.  If an update
     474             :    is to create a new record, the record is appended to the list of
     475             :    existing values as youngest without changing the order of existing
     476             :    values.  If an update erases a record in an in-prep parent, the
     477             :    erasure will be moved into the parent as the youngest without
     478             :    changing the order of existing values. */
     479             : 
     480             : static void
     481             : fd_funk_txn_update( fd_funk_t *                  funk,
     482             :                        uint *                   _dst_rec_head_idx, /* Pointer to the dst list head */
     483             :                        uint *                   _dst_rec_tail_idx, /* Pointer to the dst list tail */
     484             :                        ulong                     dst_txn_idx,       /* Transaction index of the merge destination */
     485             :                        fd_funk_txn_xid_t const * dst_xid,        /* dst xid */
     486      215984 :                        ulong                     txn_idx ) {        /* Transaction index of the records to merge */
     487      215984 :   fd_wksp_t *          wksp     = funk->wksp;
     488      215984 :   fd_alloc_t *         alloc    = funk->alloc;
     489      215984 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     490      215984 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     491      215984 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     492             : 
     493      215984 :   int critical = (_dst_rec_head_idx == &funk->shmem->rec_head_idx);
     494      215984 :   if (critical) {
     495             :     /* If the root transaction is being updated, we need to mark
     496             :        the beginning of a critical section becuase fd_funk_purify can't fix it */
     497      206552 :     fd_begin_crit(funk);
     498      206552 :   }
     499             : 
     500      215984 :   fd_funk_txn_t * txn = &txn_pool->ele[ txn_idx ];
     501      215984 :   uint rec_idx = txn->rec_head_idx;
     502     2205954 :   while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     503     1989970 :     fd_funk_rec_t * rec = &rec_pool->ele[ rec_idx ];
     504     1989970 :     uint next_rec_idx = rec->next_idx;
     505             : 
     506             :     /* See if (dst_xid,key) already exists.  */
     507     1989970 :     fd_funk_xid_key_pair_t pair[1];
     508     1989970 :     fd_funk_xid_key_pair_init( pair, dst_xid, rec->pair.key );
     509     1989970 :     for(;;) {
     510     1989970 :       fd_funk_rec_map_query_t rec_query[1];
     511     1989970 :       int err = fd_funk_rec_map_remove( rec_map, pair, NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     512     1989970 :       if( FD_UNLIKELY( err == FD_MAP_ERR_AGAIN ) ) continue;
     513     1989970 :       if( err == FD_MAP_ERR_KEY ) break;
     514      170229 :       if( FD_UNLIKELY( err != FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "map corruption" ));
     515             : 
     516             :       /* Remove from the transaction */
     517      170229 :       fd_funk_rec_t * rec2 = fd_funk_rec_map_query_ele( rec_query );
     518      170229 :       uint prev_idx = rec2->prev_idx;
     519      170229 :       uint next_idx = rec2->next_idx;
     520      170229 :       if( fd_funk_rec_idx_is_null( prev_idx ) ) {
     521        2454 :         *_dst_rec_head_idx = next_idx;
     522      167775 :       } else {
     523      167775 :         rec_pool->ele[ prev_idx ].next_idx = next_idx;
     524      167775 :       }
     525      170229 :       if( fd_funk_rec_idx_is_null( next_idx ) ) {
     526         411 :         *_dst_rec_tail_idx = prev_idx;
     527      169818 :       } else {
     528      169818 :         rec_pool->ele[ next_idx ].prev_idx = prev_idx;
     529      169818 :       }
     530             :       /* Clean up value */
     531      170229 :       fd_funk_val_flush( rec2, alloc, wksp );
     532      170229 :       rec2->txn_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     533      170229 :       fd_funk_rec_pool_release( rec_pool, rec2, 1 );
     534      170229 :       break;
     535      170229 :     }
     536             : 
     537             :     /* Add the new record to the transaction. We can update the xid in
     538             :        place because it is not used for hashing the element. We have
     539             :        to preserve the original element to preserve the
     540             :        newest-to-oldest ordering in the hash
     541             :        chain. fd_funk_rec_query_global relies on this subtle
     542             :        property. */
     543             : 
     544     1989970 :     rec->pair.xid[0] = *dst_xid;
     545     1989970 :     rec->txn_cidx = fd_funk_txn_cidx( dst_txn_idx );
     546             : 
     547     1989970 :     if( fd_funk_rec_idx_is_null( *_dst_rec_head_idx ) ) {
     548        4647 :       *_dst_rec_head_idx = rec_idx;
     549        4647 :       rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     550     1985323 :     } else {
     551     1985323 :       rec_pool->ele[ *_dst_rec_tail_idx ].next_idx = rec_idx;
     552     1985323 :       rec->prev_idx = *_dst_rec_tail_idx;
     553     1985323 :     }
     554     1989970 :     *_dst_rec_tail_idx = rec_idx;
     555     1989970 :     rec->next_idx = FD_FUNK_REC_IDX_NULL;
     556             : 
     557     1989970 :     rec_idx = next_rec_idx;
     558     1989970 :   }
     559             : 
     560      215984 :   txn_pool->ele[ txn_idx ].rec_head_idx = FD_FUNK_REC_IDX_NULL;
     561      215984 :   txn_pool->ele[ txn_idx ].rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     562             : 
     563      215984 :   if (critical) {
     564      206552 :     fd_end_crit(funk);
     565      206552 :   }
     566      215984 : }
     567             : 
     568             : /* fd_funk_txn_publish_funk_child publishes a transaction that is known
     569             :    to be a child of funk.  Callers have already validated our input
     570             :    arguments.  Returns FD_FUNK_SUCCESS on success and an FD_FUNK_ERR_*
     571             :    code on failure.  (There are currently no failure cases but the
     572             :    plumbing is there if value handling requires it at some point.) */
     573             : 
     574             : static int
     575             : fd_funk_txn_publish_funk_child( fd_funk_t * const funk,
     576             :                                 ulong       const tag,
     577      203984 :                                 ulong       const txn_idx ) {
     578             : 
     579             :   /* Apply the updates in txn to the last published transactions */
     580             : 
     581      203984 :   fd_funk_txn_update( funk, &funk->shmem->rec_head_idx, &funk->shmem->rec_tail_idx, FD_FUNK_TXN_IDX_NULL, fd_funk_root( funk ), txn_idx );
     582             : 
     583             :   /* Cancel all competing transaction histories */
     584             : 
     585      203984 :   ulong oldest_idx = fd_funk_txn_oldest_sibling( funk, txn_idx );
     586      203984 :   fd_funk_txn_cancel_sibling_list( funk, tag, oldest_idx, txn_idx );
     587             : 
     588             :   /* Make all the children children of funk */
     589             : 
     590      203984 :   fd_funk_txn_t * txn = fd_funk_txn_pool_ele( funk->txn_pool, txn_idx );
     591      203984 :   ulong child_head_idx = fd_funk_txn_idx( txn->child_head_cidx );
     592      203984 :   ulong child_tail_idx = fd_funk_txn_idx( txn->child_tail_cidx );
     593             : 
     594      203984 :   ulong child_idx = child_head_idx;
     595      386235 :   while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) { /* opt for incr pub */
     596      182251 :     fd_funk_txn_t * child_txn = fd_funk_txn_pool_ele( funk->txn_pool, child_idx );
     597      182251 :     child_txn->tag = tag;
     598      182251 :     child_txn->parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     599      182251 :     child_idx = fd_funk_txn_idx( child_txn->sibling_next_cidx );
     600      182251 :   }
     601             : 
     602      203984 :   funk->shmem->child_head_cidx = fd_funk_txn_cidx( child_head_idx );
     603      203984 :   funk->shmem->child_tail_cidx = fd_funk_txn_cidx( child_tail_idx );
     604             : 
     605      203984 :   fd_funk_txn_xid_copy( funk->shmem->last_publish, fd_funk_txn_xid( txn ) );
     606             : 
     607             :   /* Remove the mapping */
     608             : 
     609      203984 :   fd_funk_txn_map_query_t query[1];
     610      203984 :   if( fd_funk_txn_map_remove( funk->txn_map, funk->shmem->last_publish, NULL, query, FD_MAP_FLAG_BLOCKING ) == FD_MAP_SUCCESS ) {
     611      203984 :     fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     612      203984 :   }
     613             : 
     614      203984 :   return FD_FUNK_SUCCESS;
     615      203984 : }
     616             : 
     617             : ulong
     618             : fd_funk_txn_publish( fd_funk_t *     funk,
     619             :                      fd_funk_txn_t * txn,
     620      147634 :                      int             verbose ) {
     621             : 
     622             : #ifdef FD_FUNK_HANDHOLDING
     623             :   if( FD_UNLIKELY( !funk ) ) {
     624             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL funk" ));
     625             :     return 0UL;
     626             :   }
     627             :   if( FD_UNLIKELY( !fd_funk_txn_valid( funk, txn ) ) ) {
     628             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "bad txn" ));
     629             :     return 0UL;
     630             :   }
     631             : #else
     632      147634 :   (void)verbose;
     633      147634 : #endif
     634             : 
     635      147634 :   ulong txn_idx = (ulong)(txn - funk->txn_pool->ele);
     636             : 
     637      147634 :   ulong tag = funk->shmem->cycle_tag++;
     638             : 
     639      147634 :   ulong publish_stack_idx = FD_FUNK_TXN_IDX_NULL;
     640             : 
     641      203984 :   for(;;) {
     642      203984 :     fd_funk_txn_t * txn2 = &funk->txn_pool->ele[ txn_idx ];
     643      203984 :     txn2->tag = tag;
     644             : 
     645             :     /* At this point, txn_idx is a transaction that needs to be
     646             :        published and has been tagged.  If txn is a child of funk, we are
     647             :        ready to publish txn and everything on the publish stack. */
     648             : 
     649      203984 :     ulong parent_idx = fd_funk_txn_idx( txn2->parent_cidx );
     650      203984 :     if( FD_LIKELY( fd_funk_txn_idx_is_null( parent_idx ) ) ) break; /* opt for incr pub */
     651             : 
     652             :     /* txn_idx has a parent.  Validate and tag it.  Push txn to the
     653             :        publish stack and recurse into the parent. */
     654             : 
     655       56350 :     txn2->stack_cidx = fd_funk_txn_cidx( publish_stack_idx );
     656       56350 :     publish_stack_idx = txn_idx;
     657             : 
     658       56350 :     txn_idx = parent_idx;
     659       56350 :   }
     660             : 
     661      147634 :   ulong publish_cnt = 0UL;
     662             : 
     663      203984 :   for(;;) {
     664             : 
     665             :     /* At this point, all the transactions we need to publish are
     666             :        tagged, txn is the next up publish funk and publish_stack has the
     667             :        transactions to follow in order by pop.  We use a new tag for
     668             :        each publish as txn and its siblings we potentially visited in a
     669             :        previous iteration of this loop. */
     670             : 
     671      203984 :     if( FD_UNLIKELY( fd_funk_txn_publish_funk_child( funk, funk->shmem->cycle_tag++, txn_idx ) ) ) break;
     672      203984 :     publish_cnt++;
     673             : 
     674      203984 :     txn_idx = publish_stack_idx;
     675      203984 :     if( FD_LIKELY( fd_funk_txn_idx_is_null( txn_idx ) ) ) break; /* opt for incr pub */
     676       56350 :     publish_stack_idx = fd_funk_txn_idx( funk->txn_pool->ele[ txn_idx ].stack_cidx );
     677       56350 :   }
     678             : 
     679      147634 :   return publish_cnt;
     680      147634 : }
     681             : 
     682             : int
     683             : fd_funk_txn_publish_into_parent( fd_funk_t *     funk,
     684             :                                  fd_funk_txn_t * txn,
     685       12000 :                                  int             verbose ) {
     686             : #ifdef FD_FUNK_HANDHOLDING
     687             :   if( FD_UNLIKELY( !funk ) ) {
     688             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "NULL funk" ));
     689             :     return FD_FUNK_ERR_INVAL;
     690             :   }
     691             :   if( FD_UNLIKELY( !fd_funk_txn_valid( funk, txn ) ) ) {
     692             :     if( FD_UNLIKELY( verbose ) ) FD_LOG_WARNING(( "bad txn" ));
     693             :     return 0UL;
     694             :   }
     695             : #else
     696       12000 :   (void)verbose;
     697       12000 : #endif
     698             : 
     699       12000 :   fd_funk_txn_map_t *  txn_map  = funk->txn_map;
     700       12000 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     701       12000 :   ulong txn_idx = (ulong)(txn - txn_pool->ele);
     702             : 
     703       12000 :   ulong oldest_idx = fd_funk_txn_oldest_sibling( funk, txn_idx );
     704       12000 :   fd_funk_txn_cancel_sibling_list( funk, funk->shmem->cycle_tag++, oldest_idx, txn_idx );
     705             : 
     706       12000 :   ulong parent_idx = fd_funk_txn_idx( txn->parent_cidx );
     707       12000 :   if( fd_funk_txn_idx_is_null( parent_idx ) ) {
     708             :     /* Publish to root */
     709        2568 :     fd_funk_txn_update( funk, &funk->shmem->rec_head_idx, &funk->shmem->rec_tail_idx, FD_FUNK_TXN_IDX_NULL, fd_funk_root( funk ), txn_idx );
     710             :     /* Inherit the children */
     711        2568 :     funk->shmem->child_head_cidx = txn->child_head_cidx;
     712        2568 :     funk->shmem->child_tail_cidx = txn->child_tail_cidx;
     713        9432 :   } else {
     714        9432 :     fd_funk_txn_t * parent_txn = &txn_pool->ele[ parent_idx ];
     715        9432 :     fd_funk_txn_update( funk, &parent_txn->rec_head_idx, &parent_txn->rec_tail_idx, parent_idx, &parent_txn->xid, txn_idx );
     716             :     /* Inherit the children */
     717        9432 :     parent_txn->child_head_cidx = txn->child_head_cidx;
     718        9432 :     parent_txn->child_tail_cidx = txn->child_tail_cidx;
     719        9432 :   }
     720             : 
     721             :   /* Adjust the parent pointers of the children to point to their grandparent */
     722       12000 :   ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     723       21123 :   while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
     724        9123 :     txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( parent_idx );
     725        9123 :     child_idx = fd_funk_txn_idx( txn_pool->ele[ child_idx ].sibling_next_cidx );
     726        9123 :   }
     727             : 
     728       12000 :   fd_funk_txn_map_query_t query[1];
     729       12000 :   if( fd_funk_txn_map_remove( txn_map, fd_funk_txn_xid( txn ), NULL, query, FD_MAP_FLAG_BLOCKING ) == FD_MAP_SUCCESS ) {
     730       12000 :     fd_funk_txn_pool_release( txn_pool, txn, 1 );
     731       12000 :   }
     732             : 
     733       12000 :   return FD_FUNK_SUCCESS;
     734       12000 : }
     735             : 
     736             : /* Return the first record in a transaction. Returns NULL if the
     737             :    transaction has no records yet. */
     738             : 
     739             : fd_funk_rec_t const *
     740             : fd_funk_txn_first_rec( fd_funk_t *           funk,
     741    47622933 :                        fd_funk_txn_t const * txn ) {
     742    47622933 :   uint rec_idx;
     743    47622933 :   if( FD_UNLIKELY( NULL == txn ))
     744     3247731 :     rec_idx = funk->shmem->rec_head_idx;
     745    44375202 :   else
     746    44375202 :     rec_idx = txn->rec_head_idx;
     747    47622933 :   if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL;
     748    31761792 :   return funk->rec_pool->ele + rec_idx;
     749    47622933 : }
     750             : 
     751             : fd_funk_rec_t const *
     752             : fd_funk_txn_last_rec( fd_funk_t *           funk,
     753           0 :                       fd_funk_txn_t const * txn ) {
     754           0 :   uint rec_idx;
     755           0 :   if( FD_UNLIKELY( NULL == txn ))
     756           0 :     rec_idx = funk->shmem->rec_tail_idx;
     757           0 :   else
     758           0 :     rec_idx = txn->rec_tail_idx;
     759           0 :   if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL;
     760           0 :   return funk->rec_pool->ele + rec_idx;
     761           0 : }
     762             : 
     763             : /* Return the next record in a transaction. Returns NULL if the
     764             :    transaction has no more records. */
     765             : 
     766             : fd_funk_rec_t const *
     767             : fd_funk_txn_next_rec( fd_funk_t *           funk,
     768   656335248 :                       fd_funk_rec_t const * rec ) {
     769   656335248 :   uint rec_idx = rec->next_idx;
     770   656335248 :   if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL;
     771   594079938 :   return funk->rec_pool->ele + rec_idx;
     772   656335248 : }
     773             : 
     774             : fd_funk_rec_t const *
     775             : fd_funk_txn_prev_rec( fd_funk_t *           funk,
     776   318768039 :                       fd_funk_rec_t const * rec ) {
     777   318768039 :   uint rec_idx = rec->prev_idx;
     778   318768039 :   if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL;
     779   288274521 :   return funk->rec_pool->ele + rec_idx;
     780   318768039 : }
     781             : 
     782             : fd_funk_txn_xid_t
     783    18918876 : fd_funk_generate_xid(void) {
     784    18918876 :   fd_funk_txn_xid_t xid;
     785    18918876 :   static FD_TL ulong seq = 0;
     786    18918876 :   xid.ul[0] =
     787    18918876 :     (fd_log_cpu_id() + 1U)*3138831853UL +
     788    18918876 :     (fd_log_thread_id() + 1U)*9180195821UL +
     789    18918876 :     (++seq)*6208101967UL;
     790    18918876 :   xid.ul[1] = ((ulong)fd_tickcount())*2810745731UL;
     791    18918876 :   return xid;
     792    18918876 : }
     793             : 
     794             : static void
     795  4362470163 : fd_funk_txn_all_iter_skip_nulls( fd_funk_txn_all_iter_t * iter ) {
     796  4362470163 :   if( iter->chain_idx == iter->chain_cnt ) return;
     797  7157021812 :   while( fd_funk_txn_map_iter_done( iter->txn_map_iter ) ) {
     798  3000257613 :     if( ++(iter->chain_idx) == iter->chain_cnt ) break;
     799  2794551649 :     iter->txn_map_iter = fd_funk_txn_map_iter( &iter->txn_map, iter->chain_idx );
     800  2794551649 :   }
     801  4362470163 : }
     802             : 
     803             : void
     804             : fd_funk_txn_all_iter_new( fd_funk_t *              funk,
     805   205705964 :                           fd_funk_txn_all_iter_t * iter ) {
     806   205705964 :   iter->txn_map = *funk->txn_map;
     807   205705964 :   iter->chain_cnt = fd_funk_txn_map_chain_cnt( funk->txn_map );
     808   205705964 :   iter->chain_idx = 0;
     809   205705964 :   iter->txn_map_iter = fd_funk_txn_map_iter( funk->txn_map, 0 );
     810   205705964 :   fd_funk_txn_all_iter_skip_nulls( iter );
     811   205705964 : }
     812             : 
     813             : int
     814  4362470163 : fd_funk_txn_all_iter_done( fd_funk_txn_all_iter_t * iter ) {
     815  4362470163 :   return ( iter->chain_idx == iter->chain_cnt );
     816  4362470163 : }
     817             : 
     818             : void
     819  4156764199 : fd_funk_txn_all_iter_next( fd_funk_txn_all_iter_t * iter ) {
     820  4156764199 :   iter->txn_map_iter = fd_funk_txn_map_iter_next( iter->txn_map_iter );
     821  4156764199 :   fd_funk_txn_all_iter_skip_nulls( iter );
     822  4156764199 : }
     823             : 
     824             : fd_funk_txn_t const *
     825     1846548 : fd_funk_txn_all_iter_ele_const( fd_funk_txn_all_iter_t * iter ) {
     826     1846548 :   return fd_funk_txn_map_iter_ele_const( iter->txn_map_iter );
     827     1846548 : }
     828             : 
     829             : fd_funk_txn_t *
     830  4154917045 : fd_funk_txn_all_iter_ele( fd_funk_txn_all_iter_t * iter ) {
     831  4154917045 :   return fd_funk_txn_map_iter_ele( iter->txn_map_iter );
     832  4154917045 : }
     833             : 
     834             : int
     835          12 : fd_funk_txn_verify( fd_funk_t * funk ) {
     836          12 :   fd_funk_txn_map_t *  txn_map  = funk->txn_map;
     837          12 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     838          12 :   ulong                txn_max  = fd_funk_txn_pool_ele_max( txn_pool );
     839             : 
     840          12 :   ulong funk_child_head_idx = fd_funk_txn_idx( funk->shmem->child_head_cidx ); /* Previously verified */
     841          12 :   ulong funk_child_tail_idx = fd_funk_txn_idx( funk->shmem->child_tail_cidx ); /* Previously verified */
     842             : 
     843          12 :   fd_funk_txn_xid_t const * last_publish = funk->shmem->last_publish; /* Previously verified */
     844             : 
     845          24 : # define TEST(c) do {                                                                           \
     846          24 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
     847          24 :   } while(0)
     848             : 
     849          12 : # define IS_VALID( idx ) ( (idx==FD_FUNK_TXN_IDX_NULL) || \
     850          12 :                            ((idx<txn_max) && (!fd_funk_txn_xid_eq( fd_funk_txn_xid( &txn_pool->ele[idx] ), last_publish ))) )
     851             : 
     852          12 :   TEST( !fd_funk_txn_map_verify( txn_map ) );
     853          12 :   TEST( !fd_funk_txn_pool_verify( txn_pool ) );
     854             : 
     855             :   /* Tag all transactions as not visited yet */
     856             : 
     857     1572876 :   for( ulong txn_idx=0UL; txn_idx<txn_max; txn_idx++ ) txn_pool->ele[ txn_idx ].tag = 0UL;
     858             : 
     859             :   /* Visit all transactions in preparation, traversing from oldest to
     860             :      youngest. */
     861             : 
     862          12 :   do {
     863             : 
     864             :     /* Push all children of funk to the stack */
     865             : 
     866          12 :     ulong stack_idx = FD_FUNK_TXN_IDX_NULL;
     867          12 :     ulong child_idx = funk_child_head_idx;
     868          12 :     while( !fd_funk_txn_idx_is_null( child_idx ) ) {
     869             : 
     870             :       /* Make sure valid idx, not tagged (detects cycles) and child
     871             :          knows it is a child of funk.  Then tag as visited / in-prep,
     872             :          push to stack and update prep_cnt */
     873             : 
     874           0 :       TEST( IS_VALID( child_idx ) );
     875           0 :       TEST( !txn_pool->ele[ child_idx ].tag );
     876           0 :       TEST( fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn_pool->ele[ child_idx ].parent_cidx ) ) );
     877           0 :       txn_pool->ele[ child_idx ].tag        = 1UL;
     878           0 :       txn_pool->ele[ child_idx ].stack_cidx = fd_funk_txn_cidx( stack_idx );
     879           0 :       stack_idx                   = child_idx;
     880             : 
     881           0 :       ulong next_idx = fd_funk_txn_idx( txn_pool->ele[ child_idx ].sibling_next_cidx );
     882           0 :       if( !fd_funk_txn_idx_is_null( next_idx ) ) TEST( fd_funk_txn_idx( txn_pool->ele[ next_idx ].sibling_prev_cidx )==child_idx );
     883           0 :       child_idx = next_idx;
     884           0 :     }
     885             : 
     886          12 :     while( !fd_funk_txn_idx_is_null( stack_idx ) ) {
     887             : 
     888             :       /* Pop the next transaction to traverse */
     889             : 
     890           0 :       ulong txn_idx = stack_idx;
     891           0 :       stack_idx = fd_funk_txn_idx( txn_pool->ele[ txn_idx ].stack_cidx );
     892             : 
     893             :       /* Push all children of txn to the stack */
     894             : 
     895           0 :       ulong child_idx = fd_funk_txn_idx( txn_pool->ele[ txn_idx ].child_head_cidx );
     896           0 :       while( !fd_funk_txn_idx_is_null( child_idx ) ) {
     897             : 
     898             :         /* Make sure valid idx, not tagged (detects cycles) and child
     899             :            knows it is a child of txn_idx.  Then tag as visited /
     900             :            in-prep, push to stack and update prep_cnt */
     901             : 
     902           0 :         TEST( IS_VALID( child_idx ) );
     903           0 :         TEST( !txn_pool->ele[ child_idx ].tag );
     904           0 :         TEST( fd_funk_txn_idx( txn_pool->ele[ child_idx ].parent_cidx )==txn_idx );
     905           0 :         txn_pool->ele[ child_idx ].tag        = 1UL;
     906           0 :         txn_pool->ele[ child_idx ].stack_cidx = fd_funk_txn_cidx( stack_idx );
     907           0 :         stack_idx                   = child_idx;
     908             : 
     909           0 :         ulong next_idx = fd_funk_txn_idx( txn_pool->ele[ child_idx ].sibling_next_cidx );
     910           0 :         if( !fd_funk_txn_idx_is_null( next_idx ) ) TEST( fd_funk_txn_idx( txn_pool->ele[ next_idx ].sibling_prev_cidx )==child_idx );
     911           0 :         child_idx = next_idx;
     912           0 :       }
     913           0 :     }
     914             : 
     915          12 :   } while(0);
     916             : 
     917             :   /* Do it again with a youngest to oldest traversal to test reverse
     918             :      link integrity */
     919             : 
     920          12 :   do {
     921             : 
     922             :     /* Push all children of funk to the stack */
     923             : 
     924          12 :     ulong stack_idx = FD_FUNK_TXN_IDX_NULL;
     925          12 :     ulong child_idx = funk_child_tail_idx;
     926          12 :     while( !fd_funk_txn_idx_is_null( child_idx ) ) {
     927             : 
     928             :       /* Make sure valid idx, tagged as visited above (detects cycles)
     929             :          and child knows it is a child of funk.  Then tag as visited /
     930             :          in-prep, push to stack and update prep_cnt */
     931             : 
     932           0 :       TEST( IS_VALID( child_idx ) );
     933           0 :       TEST( txn_pool->ele[ child_idx ].tag==1UL );
     934           0 :       TEST( fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn_pool->ele[ child_idx ].parent_cidx ) ) );
     935           0 :       txn_pool->ele[ child_idx ].tag        = 2UL;
     936           0 :       txn_pool->ele[ child_idx ].stack_cidx = fd_funk_txn_cidx( stack_idx );
     937           0 :       stack_idx                             = child_idx;
     938             : 
     939           0 :       ulong prev_idx = fd_funk_txn_idx( txn_pool->ele[ child_idx ].sibling_prev_cidx );
     940           0 :       if( !fd_funk_txn_idx_is_null( prev_idx ) ) TEST( fd_funk_txn_idx( txn_pool->ele[ prev_idx ].sibling_next_cidx )==child_idx );
     941           0 :       child_idx = prev_idx;
     942           0 :     }
     943             : 
     944          12 :     while( !fd_funk_txn_idx_is_null( stack_idx ) ) {
     945             : 
     946             :       /* Pop the next transaction to traverse */
     947             : 
     948           0 :       ulong txn_idx = stack_idx;
     949           0 :       stack_idx = fd_funk_txn_idx( txn_pool->ele[ txn_idx ].stack_cidx );
     950             : 
     951             :       /* Push all children of txn to the stack */
     952             : 
     953           0 :       ulong child_idx = fd_funk_txn_idx( txn_pool->ele[ txn_idx ].child_tail_cidx );
     954           0 :       while( !fd_funk_txn_idx_is_null( child_idx ) ) {
     955             : 
     956             :         /* Make sure valid idx, tagged as visited above (detects cycles)
     957             :            and child knows it is a child of txn_idx.  Then, tag as
     958             :            visited / in-prep, push to stack and update prep_cnt */
     959             : 
     960           0 :         TEST( IS_VALID( child_idx ) );
     961           0 :         TEST( txn_pool->ele[ child_idx ].tag==1UL );
     962           0 :         TEST( fd_funk_txn_idx( txn_pool->ele[ child_idx ].parent_cidx )==txn_idx );
     963           0 :         txn_pool->ele[ child_idx ].tag        = 2UL;
     964           0 :         txn_pool->ele[ child_idx ].stack_cidx = fd_funk_txn_cidx( stack_idx );
     965           0 :         stack_idx                             = child_idx;
     966             : 
     967           0 :         ulong prev_idx = fd_funk_txn_idx( txn_pool->ele[ child_idx ].sibling_prev_cidx );
     968           0 :         if( !fd_funk_txn_idx_is_null( prev_idx ) ) TEST( fd_funk_txn_idx( txn_pool->ele[ prev_idx ].sibling_next_cidx )==child_idx );
     969           0 :         child_idx = prev_idx;
     970           0 :       }
     971           0 :     }
     972          12 :   } while(0);
     973             : 
     974          12 : # undef IS_VALID
     975          12 : # undef TEST
     976             : 
     977          12 :   return FD_FUNK_SUCCESS;
     978          12 : }
     979             : 
     980             : int
     981           0 : fd_funk_txn_valid( fd_funk_t const * funk, fd_funk_txn_t const * txn ) {
     982           0 :   ulong txn_idx = (ulong)(txn - funk->txn_pool->ele);
     983           0 :   ulong txn_max = fd_funk_txn_pool_ele_max( funk->txn_pool );
     984           0 :   if( txn_idx>=txn_max || txn != txn_idx + funk->txn_pool->ele ) return 0;
     985           0 :   fd_funk_txn_map_query_t query[1];
     986           0 :   if( FD_UNLIKELY( fd_funk_txn_map_query_try( funk->txn_map, &txn->xid, NULL, query, 0 ) ) ) return 0;
     987           0 :   if( fd_funk_txn_map_query_ele( query ) != txn ) return 0;
     988           0 :   return 1;
     989           0 : }

Generated by: LCOV version 1.14