LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_lineage.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 69 95 72.6 %
Date: 2026-01-24 04:58:51 Functions: 2 2 100.0 %

          Line data    Source code
       1             : #include "fd_accdb_lineage.h"
       2             : #include "../../funk/fd_funk.h"
       3             : 
       4             : void
       5             : fd_accdb_lineage_set_fork_slow( fd_accdb_lineage_t *      lineage,
       6             :                                 fd_funk_t const *         funk,
       7         129 :                                 fd_funk_txn_xid_t const * xid ) {
       8         129 :   fd_funk_txn_xid_t     next_xid = *xid;
       9         129 :   fd_funk_txn_t const * tip      = NULL;
      10             : 
      11             :   /* Walk transaction graph, recovering from overruns on-the-fly */
      12         129 :   lineage->fork_depth = 0UL;
      13             : 
      14         129 :   ulong txn_max = fd_funk_txn_pool_ele_max( funk->txn_pool );
      15         129 :   ulong i;
      16         156 :   for( i=0UL; i<FD_ACCDB_DEPTH_MAX; i++ ) {
      17         156 :     fd_funk_txn_map_query_t query[1];
      18         156 :     fd_funk_txn_t const *   candidate;
      19         156 :     fd_funk_txn_xid_t       found_xid;
      20         156 :     ulong                   parent_idx;
      21         156 :     fd_funk_txn_xid_t       parent_xid;
      22         156 : retry:
      23             :     /* Speculatively look up transaction from map */
      24         156 :     for(;;) {
      25         156 :       int query_err = fd_funk_txn_map_query_try( funk->txn_map, &next_xid, NULL, query, 0 );
      26         156 :       if( FD_UNLIKELY( query_err==FD_MAP_ERR_AGAIN ) ) {
      27             :         /* FIXME random backoff */
      28           0 :         FD_SPIN_PAUSE();
      29           0 :         continue;
      30           0 :       }
      31         156 :       if( query_err==FD_MAP_ERR_KEY ) goto done;
      32         150 :       if( FD_UNLIKELY( query_err!=FD_MAP_SUCCESS ) ) {
      33           0 :         FD_LOG_CRIT(( "fd_funk_txn_map_query_try failed: %i-%s", query_err, fd_map_strerror( query_err ) ));
      34           0 :       }
      35         150 :       break;
      36         150 :     }
      37             : 
      38             :     /* Lookup parent transaction while recovering from overruns
      39             :        FIXME This would be a lot easier if transactions specified
      40             :              parent by XID instead of by pointer ... */
      41         150 :     candidate = fd_funk_txn_map_query_ele_const( query );
      42         150 :     FD_COMPILER_MFENCE();
      43         150 :     do {
      44         150 :       found_xid  = FD_VOLATILE_CONST( candidate->xid );
      45         150 :       parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) );
      46         150 :       if( fd_funk_txn_idx_is_null( parent_idx ) ) break;
      47          27 :       if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx ));
      48             : 
      49          27 :       FD_COMPILER_MFENCE();
      50          27 :       fd_funk_txn_t const * parent = &funk->txn_pool->ele[ parent_idx ];
      51          27 :       parent_xid = FD_VOLATILE_CONST( parent->xid );
      52          27 :       FD_COMPILER_MFENCE();
      53             : 
      54          27 :       parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) );
      55          27 :       if( fd_funk_txn_idx_is_null( parent_idx ) ) break;
      56          27 :       if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx ));
      57          27 :     } while(0);
      58         150 :     FD_COMPILER_MFENCE();
      59             : 
      60             :     /* Verify speculative loads by ensuring txn still exists in map */
      61         150 :     if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
      62           0 :       FD_SPIN_PAUSE();
      63           0 :       goto retry;
      64           0 :     }
      65             : 
      66         150 :     if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &found_xid, &next_xid ) ) ) {
      67           0 :       FD_LOG_CRIT(( "fd_accdb_load_fork_slow detected memory corruption: expected xid %lu:%lu at %p, found %lu:%lu",
      68           0 :                     next_xid.ul[0], next_xid.ul[1],
      69           0 :                     (void *)candidate,
      70           0 :                     found_xid.ul[0], found_xid.ul[1] ));
      71           0 :     }
      72             : 
      73         150 :     if( !tip ) tip = candidate;  /* remember head of fork */
      74         150 :     lineage->fork[ i ] = next_xid;
      75         150 :     if( fd_funk_txn_idx_is_null( parent_idx ) ) {
      76             :       /* Reached root */
      77         123 :       i++;
      78         123 :       break;
      79         123 :     }
      80          27 :     next_xid = parent_xid;
      81          27 :   }
      82             : 
      83         129 : done:
      84         129 :   lineage->fork_depth = i;
      85         129 :   if( FD_UNLIKELY( lineage->fork_depth==FD_ACCDB_DEPTH_MAX ) ) {
      86           0 :     FD_LOG_CRIT(( "Account database fork depth exceeded max of %lu", FD_ACCDB_DEPTH_MAX ));
      87           0 :   }
      88             : 
      89             :   /* FIXME crash if fork depth greater than cache depth */
      90         129 :   if( lineage->fork_depth < FD_ACCDB_DEPTH_MAX ) {
      91         129 :     fd_funk_txn_xid_set_root( &lineage->fork[ lineage->fork_depth++ ] );
      92         129 :   }
      93             : 
      94             :   /* Remember head of fork */
      95         129 :   if( tip ) {
      96         123 :     lineage->tip_txn_idx = (ulong)( tip - funk->txn_pool->ele );
      97         123 :     fd_funk_txn_state_assert( tip, FD_FUNK_TXN_STATE_ACTIVE );
      98         123 :   } else {
      99           6 :     lineage->tip_txn_idx = ULONG_MAX;  /* XID is rooted */
     100           6 :   }
     101         129 : }
     102             : 
     103             : fd_funk_txn_t *
     104             : fd_accdb_lineage_write_check( fd_accdb_lineage_t const * lineage,
     105        6933 :                               fd_funk_t const *          funk ) {
     106        6933 :   ulong txn_idx = lineage->tip_txn_idx;
     107        6933 :   fd_funk_txn_xid_t const * xid = &lineage->fork[ 0 ];
     108        6933 :   if( FD_UNLIKELY( txn_idx==ULONG_MAX ) ) {
     109           0 :     FD_LOG_CRIT(( "write failed: XID %lu:%lu is rooted", xid->ul[0], xid->ul[1] ));
     110           0 :   }
     111        6933 :   if( FD_UNLIKELY( txn_idx >= fd_funk_txn_pool_ele_max( funk->txn_pool ) ) ) {
     112           0 :     FD_LOG_CRIT(( "memory corruption detected: invalid txn_idx %lu (max %lu)",
     113           0 :                   txn_idx, fd_funk_txn_pool_ele_max( funk->txn_pool ) ));
     114           0 :   }
     115        6933 :   fd_funk_txn_t * txn = &funk->txn_pool->ele[ txn_idx ];
     116        6933 :   if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) {
     117           0 :     FD_LOG_CRIT(( "Failed to modify account: data race detected on fork node (expected XID %lu:%lu, found %lu:%lu)",
     118           0 :                   xid->ul[0],     xid->ul[1],
     119           0 :                   txn->xid.ul[0], txn->xid.ul[1] ));
     120           0 :   }
     121        6933 :   if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
     122           0 :     FD_LOG_CRIT(( "Failed to modify account: XID %lu:%lu has children/is frozen", xid->ul[0], xid->ul[1] ));
     123           0 :   }
     124        6933 :   return txn;
     125        6933 : }

Generated by: LCOV version 1.14