LCOV - code coverage report
Current view: top level - funk - fd_funk_rec.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 182 220 82.7 %
Date: 2026-05-10 07:05:34 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "fd_funk_private.h"
       2             : #include "../util/racesan/fd_racesan_target.h"
       3             : 
       4             : /* Provide the actual record map implementation */
       5             : 
       6             : #define POOL_NAME       fd_funk_rec_pool
       7     2734038 : #define POOL_ELE_T      fd_funk_rec_t
       8             : #define POOL_IDX_T      uint
       9     2739987 : #define POOL_NEXT       map_next
      10             : #define POOL_IMPL_STYLE 2
      11             : #define POOL_LAZY       1
      12             : #include "../util/tmpl/fd_pool_para.c"
      13             : 
      14             : #define MAP_NAME              fd_funk_rec_map
      15     1499565 : #define MAP_ELE_T             fd_funk_rec_t
      16       13293 : #define MAP_KEY_T             fd_funk_xid_key_pair_t
      17     1378383 : #define MAP_KEY               pair
      18             : #define MAP_KEY_EQ(k0,k1)     fd_funk_xid_key_pair_eq((k0),(k1))
      19             : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
      20     1499094 : #define MAP_IDX_T             uint
      21     3469443 : #define MAP_NEXT              map_next
      22         108 : #define MAP_MAGIC             (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
      23     5624592 : #define MAP_CNT_WIDTH         FD_FUNK_REC_MAP_CNT_WIDTH
      24             : #define MAP_IMPL_STYLE        2
      25             : #define MAP_PEDANTIC          1
      26             : #include "../util/tmpl/fd_map_chain_para.c"
      27             : 
      28             : static fd_funk_txn_t *
      29             : fd_funk_rec_txn_borrow( fd_funk_t const *         funk,
      30             :                         fd_funk_txn_xid_t const * xid,
      31     1276968 :                         fd_funk_txn_map_query_t * query ) {
      32     1276968 :   memset( query, 0, sizeof(fd_funk_txn_map_query_t) );
      33     1276968 :   if( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) return NULL;
      34             : 
      35     1276968 :   fd_funk_txn_map_query_t txn_query[1];
      36     1276968 :   for(;;) {
      37     1276968 :     int txn_query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, txn_query, 0 );
      38     1276968 :     if( FD_UNLIKELY( txn_query_err==FD_MAP_ERR_AGAIN ) ) continue;
      39     1276968 :     if( FD_UNLIKELY( txn_query_err!=FD_MAP_SUCCESS ) ) {
      40           0 :       FD_LOG_CRIT(( "fd_funk_rec op failed: txn_map_query_try(%lu:%lu) error %i-%s",
      41           0 :                    xid->ul[0], xid->ul[1],
      42           0 :                    txn_query_err, fd_map_strerror( txn_query_err ) ));
      43           0 :     }
      44     1276968 :     break;
      45     1276968 :   }
      46     1276968 :   fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( txn_query );
      47     1276968 :   uint txn_state = FD_VOLATILE_CONST( txn->state );
      48     1276968 :   if( FD_UNLIKELY( txn_state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
      49           0 :     FD_LOG_CRIT(( "fd_funk_rec op failed: txn %p %lu:%lu state is %u-%s",
      50           0 :                   (void *)txn, xid->ul[0], xid->ul[1],
      51           0 :                   txn_state, fd_funk_txn_state_str( txn_state ) ));
      52           0 :   }
      53     1276968 :   return txn;
      54     1276968 : }
      55             : 
      56             : static void
      57     1276968 : fd_funk_rec_txn_release( fd_funk_txn_map_query_t const * query ) {
      58     1276968 :   if( !query->ele ) return;
      59           0 :   if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
      60           0 :     FD_LOG_CRIT(( "fd_funk_rec_txn_release: fd_funk_txn_map_query_test failed (data race detected?)" ));
      61           0 :   }
      62           0 : }
      63             : 
      64             : fd_funk_rec_t *
      65             : fd_funk_rec_query_try( fd_funk_t *               funk,
      66             :                        fd_funk_txn_xid_t const * xid,
      67             :                        fd_funk_rec_key_t const * key,
      68        1029 :                        fd_funk_rec_query_t *     query ) {
      69        1029 :   if( FD_UNLIKELY( !funk  ) ) FD_LOG_CRIT(( "NULL funk"  ));
      70        1029 :   if( FD_UNLIKELY( !xid   ) ) FD_LOG_CRIT(( "NULL xid"   ));
      71        1029 :   if( FD_UNLIKELY( !key   ) ) FD_LOG_CRIT(( "NULL key"   ));
      72        1029 :   if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
      73             : 
      74        1029 :   fd_funk_xid_key_pair_t pair[1];
      75        1029 :   if( FD_UNLIKELY( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) ) {
      76           0 :     fd_funk_txn_xid_set_root( pair->xid );
      77        1029 :   } else {
      78        1029 :     fd_funk_txn_xid_copy( pair->xid, xid );
      79        1029 :   }
      80        1029 :   fd_funk_rec_key_copy( pair->key, key );
      81             : 
      82        1029 :   for(;;) {
      83        1029 :     int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
      84        1029 :     if( err == FD_MAP_SUCCESS )   break;
      85          21 :     if( err == FD_MAP_ERR_KEY )   return NULL;
      86           0 :     if( err == FD_MAP_ERR_AGAIN ) continue;
      87           0 :     FD_LOG_CRIT(( "query returned err %d", err ));
      88           0 :   }
      89        1008 :   return fd_funk_rec_map_query_ele( query );
      90        1029 : }
      91             : 
      92             : fd_funk_rec_t *
      93             : fd_funk_rec_prepare( fd_funk_t *               funk,
      94             :                      fd_funk_txn_xid_t const * xid,
      95             :                      fd_funk_rec_key_t const * key,
      96             :                      fd_funk_rec_prepare_t *   prepare,
      97     1276968 :                      int *                     opt_err ) {
      98     1276968 :   if( FD_UNLIKELY( !funk    ) ) FD_LOG_CRIT(( "NULL funk"    ));
      99     1276968 :   if( FD_UNLIKELY( !xid     ) ) FD_LOG_CRIT(( "NULL xid"     ));
     100     1276968 :   if( FD_UNLIKELY( !prepare ) ) FD_LOG_CRIT(( "NULL prepare" ));
     101             : 
     102     1276968 :   fd_funk_txn_map_query_t txn_query[1];
     103     1276968 :   fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
     104             : 
     105     1276968 :   memset( prepare, 0, sizeof(fd_funk_rec_prepare_t) );
     106             : 
     107     1276968 :   if( !txn ) { /* Modifying last published */
     108           0 :     if( FD_UNLIKELY( fd_funk_last_publish_is_frozen( funk ) ) ) {
     109           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
     110           0 :       fd_funk_rec_txn_release( txn_query );
     111           0 :       return NULL;
     112           0 :     }
     113     1276968 :   } else {
     114     1276968 :     if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
     115           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
     116           0 :       fd_funk_rec_txn_release( txn_query );
     117           0 :       return NULL;
     118           0 :     }
     119     1276968 :   }
     120             : 
     121     1276968 :   fd_funk_rec_t * rec = prepare->rec = fd_funk_rec_pool_acquire( funk->rec_pool );
     122     1276968 :   if( FD_UNLIKELY( !rec ) ) {
     123           0 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_REC );
     124           0 :     fd_funk_rec_txn_release( txn_query );
     125           0 :     return rec;
     126           0 :   }
     127             : 
     128     1276968 :   fd_funk_val_init( rec );
     129     1276968 :   if( txn == NULL ) {
     130           0 :     fd_funk_txn_xid_set_root( rec->pair.xid );
     131     1276968 :   } else {
     132     1276968 :     fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
     133     1276968 :     prepare->rec_head_idx = &txn->rec_head_idx;
     134     1276968 :     prepare->rec_tail_idx = &txn->rec_tail_idx;
     135     1276968 :   }
     136     1276968 :   fd_funk_rec_key_copy( rec->pair.key, key );
     137     1276968 :   rec->tag      = 0;
     138     1276968 :   rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     139     1276968 :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     140     1276968 :   fd_funk_rec_txn_release( txn_query );
     141     1276968 :   return rec;
     142     1276968 : }
     143             : 
     144             : #if FD_HAS_ATOMIC
     145             : 
     146             : static void
     147             : fd_funk_rec_push_tail( fd_funk_t *             funk,
     148     1278666 :                        fd_funk_rec_prepare_t * prepare ) {
     149     1278666 :   fd_funk_rec_t * rec = prepare->rec;
     150     1278666 :   uint rec_idx = (uint)( rec - funk->rec_pool->ele );
     151     1278666 :   uint * rec_head_idx = prepare->rec_head_idx;
     152     1278666 :   uint * rec_tail_idx = prepare->rec_tail_idx;
     153             : 
     154     1278666 :   for(;;) {
     155             : 
     156             :     /* Doubly linked list append.  Robust in the event of concurrent
     157             :        publishes.  Iteration during publish not supported.  Sequence:
     158             :        - Identify tail element
     159             :        - Set new element's prev and next pointers
     160             :        - Set tail element's next pointer
     161             :        - Set tail pointer */
     162             : 
     163     1278666 :     uint rec_prev_idx = FD_VOLATILE_CONST( *rec_tail_idx );
     164     1278666 :     rec->prev_idx = rec_prev_idx;
     165     1278666 :     FD_COMPILER_MFENCE();
     166             : 
     167     1278666 :     uint * next_idx_p;
     168     1278666 :     if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
     169      396213 :       next_idx_p = rec_head_idx;
     170      882453 :     } else {
     171      882453 :       next_idx_p = &funk->rec_pool->ele[ rec_prev_idx ].next_idx;
     172      882453 :     }
     173             : 
     174     1278666 :     fd_racesan_hook( "funk_rec_push_tail:next_cas" );
     175     1278666 :     if( FD_UNLIKELY( !__sync_bool_compare_and_swap( next_idx_p, FD_FUNK_REC_IDX_NULL, rec_idx ) ) ) {
     176             :       /* Another thread beat us to the punch */
     177           0 :       FD_SPIN_PAUSE();
     178           0 :       continue;
     179           0 :     }
     180             : 
     181     1278666 :     fd_racesan_hook( "funk_rec_push_tail:tail_cas" );
     182     1278666 :     if( FD_UNLIKELY( !__sync_bool_compare_and_swap( rec_tail_idx, rec_prev_idx, rec_idx ) ) ) {
     183             :       /* This CAS is guaranteed to succeed if the previous CAS passed. */
     184           0 :       FD_LOG_CRIT(( "Irrecoverable data race encountered while appending to txn rec list (invariant violation?): cas(%p,%u,%u)",
     185           0 :                     (void *)rec_tail_idx, rec_prev_idx, rec_idx ));
     186           0 :     }
     187             : 
     188     1278666 :     break;
     189     1278666 :   }
     190     1278666 : }
     191             : 
     192             : #else
     193             : 
     194             : static void
     195             : fd_funk_rec_push_tail( fd_funk_t *             funk,
     196             :                        fd_funk_rec_prepare_t * prepare ) {
     197             :   fd_funk_rec_t * rec = prepare->rec;
     198             :   uint rec_idx      = (uint)( rec - funk->rec_pool->ele );
     199             :   uint * rec_head_idx = prepare->rec_head_idx;
     200             :   uint * rec_tail_idx = prepare->rec_tail_idx;
     201             :   uint rec_prev_idx = *rec_tail_idx;
     202             :   *rec_tail_idx = rec_idx;
     203             :   rec->prev_idx = rec_prev_idx;
     204             :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     205             :   if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
     206             :     *rec_head_idx = rec_idx;
     207             :   } else {
     208             :     funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
     209             :   }
     210             : }
     211             : 
     212             : #endif
     213             : 
     214             : void
     215             : fd_funk_rec_publish( fd_funk_t *             funk,
     216     1365075 :                      fd_funk_rec_prepare_t * prepare ) {
     217     1365075 :   fd_funk_rec_t * rec = prepare->rec;
     218     1365075 :   rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     219     1365075 :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     220             : 
     221     1365075 :   if( prepare->rec_head_idx ) {
     222     1278666 :     fd_funk_rec_push_tail( funk, prepare );
     223     1278666 :   }
     224             : 
     225     1365075 :   fd_racesan_hook( "funk_rec_publish:map_insert" );
     226     1365075 :   int insert_err = fd_funk_rec_map_insert( funk->rec_map, rec, FD_MAP_FLAG_BLOCKING );
     227     1365075 :   if( insert_err ) {
     228           0 :     FD_LOG_CRIT(( "fd_funk_rec_map_insert failed (%i-%s)", insert_err, fd_map_strerror( insert_err ) ));
     229           0 :   }
     230     1365075 : }
     231             : 
     232             : int
     233         201 : fd_funk_rec_verify( fd_funk_t * funk ) {
     234         201 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     235         201 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     236         201 :   ulong rec_max = fd_funk_rec_pool_ele_max( rec_pool );
     237             : 
     238      498816 : # define TEST(c) do {                                                                           \
     239      498816 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
     240      498816 :   } while(0)
     241             : 
     242         201 :   TEST( !fd_funk_rec_map_verify( rec_map ) );
     243         201 :   TEST( !fd_funk_rec_pool_verify( rec_pool ) );
     244             : 
     245             :   /* Iterate (again) over all records in use */
     246             : 
     247         201 :   fd_funk_rec_map_shmem_private_chain_t * chain_tbl =
     248         201 :     fd_funk_rec_map_shmem_private_chain( rec_map->map, 0UL );
     249         201 :   ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
     250      444105 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     251      443904 :     ulong chain_ele_cnt = fd_funk_rec_map_private_vcnt_cnt( chain_tbl[ chain_idx ].ver_cnt );
     252      443904 :     ulong chain_ele_idx = 0UL;
     253      443904 :     for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
     254      457197 :          !fd_funk_rec_map_iter_done( iter );
     255      443904 :          iter = fd_funk_rec_map_iter_next( iter ), chain_ele_idx++ ) {
     256       13293 :       fd_funk_rec_t const * rec = fd_funk_rec_map_iter_ele_const( iter );
     257             : 
     258             :       /* Make sure every record either links up with the last published
     259             :          transaction or an in-prep transaction and the flags are sane. */
     260             : 
     261       13293 :       fd_funk_txn_xid_t const * xid = fd_funk_rec_xid( rec );
     262             : 
     263       13293 :       if( fd_funk_txn_xid_eq_root( xid ) ) { /* This is a record from the last published transaction */
     264             : 
     265       12288 :         TEST( fd_funk_txn_xid_eq_root( xid ) );
     266             :         /* No record linked list at the root txn */
     267       12288 :         TEST( fd_funk_rec_idx_is_null( rec->prev_idx ) );
     268       12288 :         TEST( fd_funk_rec_idx_is_null( rec->next_idx ) );
     269             : 
     270       12288 :       } else { /* This is a record from an in-prep transaction */
     271             : 
     272        1005 :         fd_funk_txn_map_query_t query[1];
     273        1005 :         TEST( fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 )==FD_MAP_SUCCESS );
     274             : 
     275        1005 :       }
     276       13293 :     }
     277      443904 :     TEST( chain_ele_idx==chain_ele_cnt );
     278      443904 :   }
     279             : 
     280             :   /* Clear record tags and then verify membership */
     281             : 
     282      888009 :   for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool->ele[ rec_idx ].tag = 0;
     283             : 
     284         201 :   do {
     285         201 :     fd_funk_all_iter_t iter[1];
     286       13494 :     for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     287       13293 :       fd_funk_rec_t * rec = fd_funk_all_iter_ele( iter );
     288       13293 :       if( fd_funk_txn_xid_eq_root( rec->pair.xid ) ) {
     289       12288 :         TEST( !rec->tag );
     290       12288 :         rec->tag = 1;
     291       12288 :       }
     292       13293 :     }
     293             : 
     294         201 :     fd_funk_txn_all_iter_t txn_iter[1];
     295         789 :     for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
     296         588 :       fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
     297             : 
     298         588 :       uint rec_idx = txn->rec_head_idx;
     299        1593 :       while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     300        1005 :         TEST( (rec_idx<rec_max) && !rec_pool->ele[ rec_idx ].tag );
     301        1005 :         rec_pool->ele[ rec_idx ].tag = 1;
     302        1005 :         fd_funk_rec_query_t query[1];
     303        1005 :         fd_funk_rec_t const * rec2 = fd_funk_rec_query_try( funk, &txn->xid, rec_pool->ele[ rec_idx ].pair.key, query );
     304        1005 :         TEST( rec2 == rec_pool->ele + rec_idx );
     305        1005 :         uint next_idx = rec_pool->ele[ rec_idx ].next_idx;
     306        1005 :         if( !fd_funk_rec_idx_is_null( next_idx ) ) TEST( rec_pool->ele[ next_idx ].prev_idx==rec_idx );
     307        1005 :         rec_idx = next_idx;
     308        1005 :       }
     309         588 :     }
     310         201 :   } while(0);
     311             : 
     312         201 :   do {
     313         201 :     fd_funk_txn_all_iter_t txn_iter[1];
     314         789 :     for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
     315         588 :       fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
     316             : 
     317         588 :       uint  rec_idx = txn->rec_tail_idx;
     318        1593 :       while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     319        1005 :         TEST( (rec_idx<rec_max) && rec_pool->ele[ rec_idx ].tag );
     320        1005 :         rec_pool->ele[ rec_idx ].tag = 0;
     321        1005 :         uint prev_idx = rec_pool->ele[ rec_idx ].prev_idx;
     322        1005 :         if( !fd_funk_rec_idx_is_null( prev_idx ) ) TEST( rec_pool->ele[ prev_idx ].next_idx==rec_idx );
     323        1005 :         rec_idx = prev_idx;
     324        1005 :       }
     325         588 :     }
     326         201 :   } while(0);
     327             : 
     328         201 :   fd_funk_all_iter_t iter[1];
     329       13494 :   for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     330       13293 :     fd_funk_rec_t const * rec = fd_funk_all_iter_ele( iter );
     331       13293 :     FD_TEST( rec->tag == fd_funk_txn_xid_eq_root( rec->pair.xid ) );
     332       13293 :   }
     333             : 
     334         201 : # undef TEST
     335             : 
     336         201 :   return FD_FUNK_SUCCESS;
     337         201 : }

Generated by: LCOV version 1.14