LCOV - code coverage report
Current view: top level - funk - fd_funk_rec.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 211 356 59.3 %
Date: 2025-10-13 04:42:14 Functions: 12 15 80.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     4358223 : #define POOL_ELE_T         fd_funk_rec_t
       8             : #define POOL_IDX_T         uint
       9     7709352 : #define POOL_NEXT          map_next
      10             : #define POOL_IMPL_STYLE    2
      11             : #include "../util/tmpl/fd_pool_para.c"
      12             : 
      13             : #define MAP_NAME              fd_funk_rec_map
      14   639994143 : #define MAP_ELE_T             fd_funk_rec_t
      15           0 : #define MAP_KEY_T             fd_funk_xid_key_pair_t
      16     2178999 : #define MAP_KEY               pair
      17             : #define MAP_KEY_EQ(k0,k1)     fd_funk_xid_key_pair_eq((k0),(k1))
      18             : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
      19   639994065 : #define MAP_IDX_T             uint
      20   772588977 : #define MAP_NEXT              map_next
      21          42 : #define MAP_MAGIC             (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
      22             : #define MAP_IMPL_STYLE        2
      23             : #include "../util/tmpl/fd_map_chain_para.c"
      24             : 
      25             : static fd_funk_txn_t *
      26             : fd_funk_rec_txn_borrow( fd_funk_t const *         funk,
      27             :                         fd_funk_txn_xid_t const * xid,
      28    61586682 :                         fd_funk_txn_map_query_t * query ) {
      29    61586682 :   memset( query, 0, sizeof(fd_funk_txn_map_query_t) );
      30    61586682 :   if( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) return NULL;
      31             : 
      32    50652795 :   fd_funk_txn_map_query_t txn_query[1];
      33    50652795 :   for(;;) {
      34    50652795 :     int txn_query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, txn_query, 0 );
      35    50652795 :     if( FD_UNLIKELY( txn_query_err==FD_MAP_ERR_AGAIN ) ) continue;
      36    50652795 :     if( FD_UNLIKELY( txn_query_err!=FD_MAP_SUCCESS ) ) {
      37           0 :       FD_LOG_CRIT(( "fd_funk_rec op failed: txn_map_query_try(%lu:%lu) error %i-%s",
      38           0 :                    xid->ul[0], xid->ul[1],
      39           0 :                    txn_query_err, fd_map_strerror( txn_query_err ) ));
      40           0 :     }
      41    50652795 :     break;
      42    50652795 :   }
      43    50652795 :   fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( txn_query );
      44    50652795 :   uint txn_state = FD_VOLATILE_CONST( txn->state );
      45    50652795 :   if( FD_UNLIKELY( txn_state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
      46           0 :     FD_LOG_CRIT(( "fd_funk_rec op failed: txn %p %lu:%lu state is %u-%s",
      47           0 :                   (void *)txn, xid->ul[0], xid->ul[1],
      48           0 :                   txn_state, fd_funk_txn_state_str( txn_state ) ));
      49           0 :   }
      50    50652795 :   return txn;
      51    50652795 : }
      52             : 
      53             : static void
      54    61586682 : fd_funk_rec_txn_release( fd_funk_txn_map_query_t const * query ) {
      55    61586682 :   if( !query->ele ) return;
      56           0 :   if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
      57           0 :     FD_LOG_CRIT(( "fd_funk_rec_txn_release: fd_funk_txn_map_query_test failed (data race detected?)" ));
      58           0 :   }
      59           0 : }
      60             : 
      61             : static void
      62             : fd_funk_rec_key_set_pair( fd_funk_xid_key_pair_t *  key_pair,
      63             :                           fd_funk_txn_xid_t const * xid,
      64    59407638 :                           fd_funk_rec_key_t const * key ) {
      65    59407638 :   fd_funk_txn_xid_copy( key_pair->xid, xid );
      66    59407638 :   fd_funk_rec_key_copy( key_pair->key, key );
      67    59407638 : }
      68             : 
      69             : fd_funk_rec_t const *
      70             : fd_funk_rec_query_try( fd_funk_t *               funk,
      71             :                        fd_funk_txn_xid_t const * xid,
      72             :                        fd_funk_rec_key_t const * key,
      73   637764174 :                        fd_funk_rec_query_t *     query ) {
      74   637764174 :   if( FD_UNLIKELY( !funk  ) ) FD_LOG_CRIT(( "NULL funk"  ));
      75   637764174 :   if( FD_UNLIKELY( !xid   ) ) FD_LOG_CRIT(( "NULL xid"   ));
      76   637764174 :   if( FD_UNLIKELY( !key   ) ) FD_LOG_CRIT(( "NULL key"   ));
      77   637764174 :   if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
      78             : 
      79   637764174 :   fd_funk_xid_key_pair_t pair[1];
      80   637764174 :   if( FD_UNLIKELY( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) ) {
      81       58809 :     fd_funk_txn_xid_set_root( pair->xid );
      82   637705365 :   } else {
      83   637705365 :     fd_funk_txn_xid_copy( pair->xid, xid );
      84   637705365 :   }
      85   637764174 :   fd_funk_rec_key_copy( pair->key, key );
      86             : 
      87   637764174 :   for(;;) {
      88   637764174 :     int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
      89   637764174 :     if( err == FD_MAP_SUCCESS )   break;
      90          45 :     if( err == FD_MAP_ERR_KEY )   return NULL;
      91           0 :     if( err == FD_MAP_ERR_AGAIN ) continue;
      92           0 :     FD_LOG_CRIT(( "query returned err %d", err ));
      93           0 :   }
      94   637764129 :   return fd_funk_rec_map_query_ele_const( query );
      95   637764174 : }
      96             : 
      97             : 
      98             : fd_funk_rec_t *
      99             : fd_funk_rec_modify( fd_funk_t *               funk,
     100             :                     fd_funk_txn_xid_t const * xid,
     101             :                     fd_funk_rec_key_t const * key,
     102           0 :                     fd_funk_rec_query_t *     query ) {
     103           0 :   fd_funk_rec_map_t *    rec_map = fd_funk_rec_map( funk );
     104           0 :   fd_funk_xid_key_pair_t pair[1];
     105           0 :   fd_funk_rec_key_set_pair( pair, xid, key );
     106             : 
     107           0 :   int err = fd_funk_rec_map_modify_try( rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     108           0 :   if( err==FD_MAP_ERR_KEY ) return NULL;
     109           0 :   if( err!=FD_MAP_SUCCESS ) FD_LOG_CRIT(( "query returned err %d", err ));
     110             : 
     111           0 :   fd_funk_rec_t * rec = fd_funk_rec_map_query_ele( query );
     112           0 :   return rec;
     113           0 : }
     114             : 
     115             : void
     116           0 : fd_funk_rec_modify_publish( fd_funk_rec_query_t * query ) {
     117           0 :   fd_funk_rec_map_modify_test( query );
     118           0 : }
     119             : 
     120             : fd_funk_rec_t const *
     121             : fd_funk_rec_query_try_global( fd_funk_t const *         funk,
     122             :                               fd_funk_txn_xid_t const * xid,
     123             :                               fd_funk_rec_key_t const * key,
     124             :                               fd_funk_txn_xid_t *       txn_out,
     125    59407638 :                               fd_funk_rec_query_t *     query ) {
     126    59407638 :   if( FD_UNLIKELY( !funk  ) ) FD_LOG_CRIT(( "NULL funk"  ));
     127    59407638 :   if( FD_UNLIKELY( !key   ) ) FD_LOG_CRIT(( "NULL key"   ));
     128    59407638 :   if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
     129             : 
     130             :   /* Attach to the funk transaction */
     131             : 
     132    59407638 :   fd_funk_txn_map_query_t txn_query[1];
     133    59407638 :   fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
     134             : 
     135             :   /* Look for the first element in the hash chain with the right
     136             :      record key. This takes advantage of the fact that elements with
     137             :      the same record key appear on the same hash chain in order of
     138             :      newest to oldest. */
     139             : 
     140    59407638 :   fd_funk_xid_key_pair_t pair[1];
     141    59407638 :   fd_funk_rec_key_set_pair( pair, xid, key );
     142             : 
     143    59407638 :   fd_funk_rec_map_t const * rec_map = funk->rec_map;
     144             : 
     145    59407638 :   ulong hash = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
     146    59407638 :   ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
     147    59407638 :   fd_funk_rec_map_shmem_private_chain_t * chain = fd_funk_rec_map_shmem_private_chain( rec_map->map, hash );
     148             : 
     149             :   /* Start a speculative record map transaction */
     150             : 
     151    59407638 :   struct {
     152    59407638 :     fd_funk_rec_map_txn_t txn;
     153    59407638 :     fd_funk_rec_map_txn_private_info_t lock[1];
     154    59407638 :   } txn_mem;
     155    59407638 :   fd_funk_rec_map_txn_t * rec_txn;
     156    59407638 :   fd_funk_rec_t const * res;
     157             : 
     158    59407638 : retry:
     159    59407638 :   res = NULL;
     160    59407638 :   rec_txn = fd_funk_rec_map_txn_init( &txn_mem.txn, (void *)rec_map, 1UL );
     161    59407638 :   txn_mem.lock[ 0 ].chain = chain;
     162    59407638 :   txn_mem.txn.spec_cnt++;
     163    59407638 :   if( FD_UNLIKELY( fd_funk_rec_map_txn_try( rec_txn, FD_MAP_FLAG_BLOCKING )!=FD_MAP_SUCCESS ) ) {
     164           0 :     FD_LOG_CRIT(( "fd_funk_rec_map_txn_try failed" ));
     165           0 :   }
     166             : 
     167    59407638 :   query->ele     = NULL;
     168    59407638 :   query->chain   = chain;
     169    59407638 :   query->ver_cnt = txn_mem.lock[0].ver_cnt;
     170             : 
     171    59407638 :   for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( funk->rec_map, chain_idx );
     172   145188219 :        !fd_funk_rec_map_iter_done( iter );
     173   145152618 :        iter = fd_funk_rec_map_iter_next( iter ) ) {
     174   145152618 :     fd_funk_rec_t const * ele = fd_funk_rec_map_iter_ele_const( iter );
     175   145152618 :     if( FD_LIKELY( fd_funk_rec_key_eq( key, ele->pair.key ) ) ) {
     176             : 
     177             :       /* For cur_txn in path from [txn] to [root] where root is NULL */
     178             : 
     179   289088853 :       for( fd_funk_txn_t const * cur_txn = txn; ; cur_txn = fd_funk_txn_parent( cur_txn, funk->txn_pool ) ) {
     180             :         /* If record ele is part of transaction cur_txn, we have a
     181             :            match. According to the property above, this will be the
     182             :            youngest descendent in the transaction stack. */
     183             : 
     184   289088853 :         int match = FD_UNLIKELY( cur_txn ) ? /* opt for root find (FIXME: eliminate branch with cmov into txn_xid_eq?) */
     185   202187634 :           fd_funk_txn_xid_eq( &cur_txn->xid, ele->pair.xid ) :
     186   289088853 :           fd_funk_txn_xid_eq_root( ele->pair.xid );
     187             : 
     188   289088853 :         if( FD_LIKELY( match ) ) {
     189    59372037 :           if( txn_out ) {
     190           0 :             *txn_out = *( cur_txn ? &cur_txn->xid : fd_funk_last_publish( funk ) );
     191           0 :           }
     192    59372037 :           query->ele = (fd_funk_rec_t *)ele;
     193    59372037 :           res = query->ele;
     194    59372037 :           goto found;
     195    59372037 :         }
     196             : 
     197   229716816 :         if( cur_txn == NULL ) break;
     198   229716816 :       }
     199    98418258 :     }
     200   145152618 :   }
     201             : 
     202    59407638 : found:
     203    59407638 :   if( FD_UNLIKELY( fd_funk_rec_map_txn_test( rec_txn )!=FD_MAP_SUCCESS ) ) goto retry;
     204    59407638 :   if( FD_LIKELY( txn ) ) fd_funk_txn_xid_assert( txn, pair->xid );
     205    59407638 :   fd_funk_rec_txn_release( txn_query );
     206    59407638 :   return res;
     207    59407638 : }
     208             : 
     209             : fd_funk_rec_t const *
     210             : fd_funk_rec_query_copy( fd_funk_t *               funk,
     211             :                         fd_funk_txn_xid_t const * xid,
     212             :                         fd_funk_rec_key_t const * key,
     213             :                         fd_valloc_t               valloc,
     214           0 :                         ulong *                   sz_out ) {
     215           0 :   *sz_out = ULONG_MAX;
     216           0 :   fd_funk_xid_key_pair_t pair[1];
     217           0 :   fd_funk_rec_key_set_pair( pair, xid, key );
     218             : 
     219           0 :   void * last_copy = NULL;
     220           0 :   ulong last_copy_sz = 0;
     221           0 :   for(;;) {
     222           0 :     fd_funk_rec_query_t query[1];
     223           0 :     int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
     224           0 :     if( err == FD_MAP_ERR_KEY )   {
     225           0 :       if( last_copy ) fd_valloc_free( valloc, last_copy );
     226           0 :       return NULL;
     227           0 :     }
     228           0 :     if( err == FD_MAP_ERR_AGAIN ) continue;
     229           0 :     if( err != FD_MAP_SUCCESS )   FD_LOG_CRIT(( "query returned err %d", err ));
     230           0 :     fd_funk_rec_t const * rec = fd_funk_rec_map_query_ele_const( query );
     231           0 :     ulong sz = fd_funk_val_sz( rec );
     232           0 :     void * copy;
     233           0 :     if( sz <= last_copy_sz ) {
     234           0 :       copy = last_copy;
     235           0 :     } else {
     236           0 :       if( last_copy ) fd_valloc_free( valloc, last_copy );
     237           0 :       copy = last_copy = fd_valloc_malloc( valloc, 1, sz );
     238           0 :       last_copy_sz = sz;
     239           0 :     }
     240           0 :     memcpy( copy, fd_funk_val( rec, fd_funk_wksp( funk ) ), sz );
     241           0 :     *sz_out = sz;
     242           0 :     if( !fd_funk_rec_query_test( query ) ) return copy;
     243           0 :   }
     244           0 : }
     245             : 
     246             : int
     247   697167543 : fd_funk_rec_query_test( fd_funk_rec_query_t * query ) {
     248   697167543 :   return fd_funk_rec_map_query_test( query );
     249   697167543 : }
     250             : 
     251             : fd_funk_rec_t *
     252             : fd_funk_rec_prepare( fd_funk_t *               funk,
     253             :                      fd_funk_txn_xid_t const * xid,
     254             :                      fd_funk_rec_key_t const * key,
     255             :                      fd_funk_rec_prepare_t *   prepare,
     256     2179044 :                      int *                     opt_err ) {
     257     2179044 :   if( FD_UNLIKELY( !funk    ) ) FD_LOG_CRIT(( "NULL funk"    ));
     258     2179044 :   if( FD_UNLIKELY( !xid     ) ) FD_LOG_CRIT(( "NULL xid"     ));
     259     2179044 :   if( FD_UNLIKELY( !prepare ) ) FD_LOG_CRIT(( "NULL prepare" ));
     260             : 
     261     2179044 :   fd_funk_txn_map_query_t txn_query[1];
     262     2179044 :   fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
     263             : 
     264     2179044 :   memset( prepare, 0, sizeof(fd_funk_rec_prepare_t) );
     265             : 
     266     2179044 :   if( !txn ) { /* Modifying last published */
     267         306 :     if( FD_UNLIKELY( fd_funk_last_publish_is_frozen( funk ) ) ) {
     268           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
     269           0 :       fd_funk_rec_txn_release( txn_query );
     270           0 :       return NULL;
     271           0 :     }
     272     2178738 :   } else {
     273     2178738 :     if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
     274           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
     275           0 :       fd_funk_rec_txn_release( txn_query );
     276           0 :       return NULL;
     277           0 :     }
     278     2178738 :   }
     279             : 
     280     2179044 :   fd_funk_rec_t * rec = prepare->rec = fd_funk_rec_pool_acquire( funk->rec_pool, NULL, 1, opt_err );
     281     2179044 :   if( opt_err && *opt_err == FD_POOL_ERR_CORRUPT ) {
     282           0 :     FD_LOG_CRIT(( "corrupt element returned from funk rec pool" ));
     283           0 :   }
     284     2179044 :   if( FD_UNLIKELY( !rec ) ) {
     285           0 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_REC );
     286           0 :     fd_funk_rec_txn_release( txn_query );
     287           0 :     return rec;
     288           0 :   }
     289             : 
     290     2179044 :   fd_funk_val_init( rec );
     291     2179044 :   if( txn == NULL ) {
     292         306 :     fd_funk_txn_xid_set_root( rec->pair.xid );
     293     2178738 :   } else {
     294     2178738 :     fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
     295     2178738 :     prepare->rec_head_idx = &txn->rec_head_idx;
     296     2178738 :     prepare->rec_tail_idx = &txn->rec_tail_idx;
     297     2178738 :   }
     298     2179044 :   fd_funk_rec_key_copy( rec->pair.key, key );
     299     2179044 :   rec->tag      = 0;
     300     2179044 :   rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     301     2179044 :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     302     2179044 :   fd_funk_rec_txn_release( txn_query );
     303     2179044 :   return rec;
     304     2179044 : }
     305             : 
     306             : #if FD_HAS_ATOMIC
     307             : 
     308             : static void
     309             : fd_funk_rec_push_tail( fd_funk_t *             funk,
     310     2178702 :                        fd_funk_rec_prepare_t * prepare ) {
     311     2178702 :   fd_funk_rec_t * rec = prepare->rec;
     312     2178702 :   uint rec_idx = (uint)( rec - funk->rec_pool->ele );
     313     2178702 :   uint * rec_head_idx = prepare->rec_head_idx;
     314     2178702 :   uint * rec_tail_idx = prepare->rec_tail_idx;
     315             : 
     316     2178702 :   for(;;) {
     317             : 
     318             :     /* Doubly linked list append.  Robust in the event of concurrent
     319             :        publishes.  Iteration during publish not supported.  Sequence:
     320             :        - Identify tail element
     321             :        - Set new element's prev and next pointers
     322             :        - Set tail element's next pointer
     323             :        - Set tail pointer */
     324             : 
     325     2178702 :     uint rec_prev_idx = FD_VOLATILE_CONST( *rec_tail_idx );
     326     2178702 :     rec->prev_idx = rec_prev_idx;
     327     2178702 :     FD_COMPILER_MFENCE();
     328             : 
     329     2178702 :     uint * next_idx_p;
     330     2178702 :     if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
     331      388455 :       next_idx_p = rec_head_idx;
     332     1790247 :     } else {
     333     1790247 :       next_idx_p = &funk->rec_pool->ele[ rec_prev_idx ].next_idx;
     334     1790247 :     }
     335             : 
     336     2178702 :     fd_racesan_hook( "funk_rec_push_tail:next_cas" );
     337     2178702 :     if( FD_UNLIKELY( !__sync_bool_compare_and_swap( next_idx_p, FD_FUNK_REC_IDX_NULL, rec_idx ) ) ) {
     338             :       /* Another thread beat us to the punch */
     339           0 :       FD_SPIN_PAUSE();
     340           0 :       continue;
     341           0 :     }
     342             : 
     343     2178702 :     fd_racesan_hook( "funk_rec_push_tail:tail_cas" );
     344     2178702 :     if( FD_UNLIKELY( !__sync_bool_compare_and_swap( rec_tail_idx, rec_prev_idx, rec_idx ) ) ) {
     345             :       /* This CAS is guaranteed to succeed if the previous CAS passed. */
     346           0 :       FD_LOG_CRIT(( "Irrecoverable data race encountered while appending to txn rec list (invariant violation?): cas(%p,%u,%u)",
     347           0 :                     (void *)rec_tail_idx, rec_prev_idx, rec_idx ));
     348           0 :     }
     349             : 
     350     2178702 :     break;
     351     2178702 :   }
     352     2178702 : }
     353             : 
     354             : #else
     355             : 
     356             : static void
     357             : fd_funk_rec_push_tail( fd_funk_t *             funk,
     358             :                        fd_funk_rec_prepare_t * prepare ) {
     359             :   fd_funk_rec_t * rec = prepare->rec;
     360             :   uint rec_idx      = (uint)( rec - funk->rec_pool->ele );
     361             :   uint * rec_head_idx = prepare->rec_head_idx;
     362             :   uint * rec_tail_idx = prepare->rec_tail_idx;
     363             :   uint rec_prev_idx = *rec_tail_idx;
     364             :   *rec_tail_idx = rec_idx;
     365             :   rec->prev_idx = rec_prev_idx;
     366             :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     367             :   if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
     368             :     *rec_head_idx = rec_idx;
     369             :   } else {
     370             :     funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
     371             :   }
     372             : }
     373             : 
     374             : #endif
     375             : 
     376             : void
     377             : fd_funk_rec_publish( fd_funk_t *             funk,
     378     2178999 :                      fd_funk_rec_prepare_t * prepare ) {
     379     2178999 :   fd_funk_rec_t * rec = prepare->rec;
     380     2178999 :   rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     381     2178999 :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     382             : 
     383     2178999 :   if( prepare->rec_head_idx ) {
     384     2178702 :     fd_funk_rec_push_tail( funk, prepare );
     385     2178702 :   }
     386             : 
     387     2178999 :   fd_racesan_hook( "funk_rec_publish:map_insert" );
     388     2178999 :   int insert_err = fd_funk_rec_map_insert( funk->rec_map, rec, FD_MAP_FLAG_BLOCKING );
     389     2178999 :   if( insert_err ) {
     390           0 :     FD_LOG_CRIT(( "fd_funk_rec_map_insert failed (%i-%s)", insert_err, fd_map_strerror( insert_err ) ));
     391           0 :   }
     392     2178999 : }
     393             : 
     394             : void
     395             : fd_funk_rec_cancel( fd_funk_t *             funk,
     396          45 :                     fd_funk_rec_prepare_t * prepare ) {
     397          45 :   fd_funk_val_flush( prepare->rec, funk->alloc, funk->wksp );
     398          45 :   fd_funk_rec_pool_release( funk->rec_pool, prepare->rec, 1 );
     399          45 : }
     400             : 
     401             : fd_funk_rec_t *
     402             : fd_funk_rec_clone( fd_funk_t *               funk,
     403             :                    fd_funk_txn_xid_t const * xid,
     404             :                    fd_funk_rec_key_t const * key,
     405             :                    fd_funk_rec_prepare_t *   prepare,
     406          45 :                    int *                     opt_err ) {
     407          45 :   fd_funk_rec_t * new_rec = fd_funk_rec_prepare( funk, xid, key, prepare, opt_err );
     408          45 :   if( !new_rec ) return NULL;
     409             : 
     410          45 :   for(;;) {
     411          45 :     fd_funk_rec_query_t query[1];
     412          45 :     fd_funk_rec_t const * old_rec = fd_funk_rec_query_try_global( funk, xid, key, NULL, query );
     413          45 :     if( !old_rec ) {
     414          45 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_KEY );
     415          45 :       fd_funk_rec_cancel( funk, prepare );
     416          45 :       return NULL;
     417          45 :     }
     418             : 
     419           0 :     fd_wksp_t * wksp = fd_funk_wksp( funk );
     420           0 :     ulong val_sz     = old_rec->val_sz;
     421           0 :     void * buf = fd_funk_val_truncate(
     422           0 :         new_rec,
     423           0 :         fd_funk_alloc( funk ),
     424           0 :         wksp,
     425           0 :         0UL,
     426           0 :         val_sz,
     427           0 :         opt_err );
     428           0 :     if( !buf ) {
     429           0 :       fd_funk_rec_cancel( funk, prepare );
     430           0 :       return NULL;
     431           0 :     }
     432           0 :     memcpy( buf, fd_funk_val( old_rec, wksp ), val_sz );
     433             : 
     434           0 :     if( !fd_funk_rec_query_test( query ) ) {
     435           0 :       return new_rec;
     436           0 :     }
     437           0 :   }
     438          45 : }
     439             : 
     440             : int
     441          12 : fd_funk_rec_verify( fd_funk_t * funk ) {
     442          12 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     443          12 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     444          12 :   ulong rec_max = fd_funk_rec_pool_ele_max( rec_pool );
     445             : 
     446          24 : # define TEST(c) do {                                                                           \
     447          24 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
     448          24 :   } while(0)
     449             : 
     450          12 :   TEST( !fd_funk_rec_map_verify( rec_map ) );
     451          12 :   TEST( !fd_funk_rec_pool_verify( rec_pool ) );
     452             : 
     453             :   /* Iterate (again) over all records in use */
     454             : 
     455          12 :   ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
     456      786450 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     457      786438 :     for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
     458      786438 :          !fd_funk_rec_map_iter_done( iter );
     459      786438 :          iter = fd_funk_rec_map_iter_next( iter ) ) {
     460           0 :       fd_funk_rec_t const * rec = fd_funk_rec_map_iter_ele_const( iter );
     461             : 
     462             :       /* Make sure every record either links up with the last published
     463             :          transaction or an in-prep transaction and the flags are sane. */
     464             : 
     465           0 :       fd_funk_txn_xid_t const * xid = fd_funk_rec_xid( rec );
     466             : 
     467           0 :       if( fd_funk_txn_xid_eq_root( xid ) ) { /* This is a record from the last published transaction */
     468             : 
     469           0 :         TEST( fd_funk_txn_xid_eq_root( xid ) );
     470             :         /* No record linked list at the root txn */
     471           0 :         TEST( fd_funk_rec_idx_is_null( rec->prev_idx ) );
     472           0 :         TEST( fd_funk_rec_idx_is_null( rec->next_idx ) );
     473             : 
     474           0 :       } else { /* This is a record from an in-prep transaction */
     475             : 
     476           0 :         TEST( fd_funk_txn_query( xid, funk->txn_map ) );
     477             : 
     478           0 :       }
     479           0 :     }
     480      786438 :   }
     481             : 
     482             :   /* Clear record tags and then verify membership */
     483             : 
     484     1572876 :   for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool->ele[ rec_idx ].tag = 0;
     485             : 
     486          12 :   do {
     487          12 :     fd_funk_all_iter_t iter[1];
     488          12 :     for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     489           0 :       fd_funk_rec_t * rec = fd_funk_all_iter_ele( iter );
     490           0 :       if( fd_funk_txn_xid_eq_root( rec->pair.xid ) ) {
     491           0 :         TEST( !rec->tag );
     492           0 :         rec->tag = 1;
     493           0 :       }
     494           0 :     }
     495             : 
     496          12 :     fd_funk_txn_all_iter_t txn_iter[1];
     497          12 :     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 ) ) {
     498           0 :       fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
     499             : 
     500           0 :       uint rec_idx = txn->rec_head_idx;
     501           0 :       while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     502           0 :         TEST( (rec_idx<rec_max) && !rec_pool->ele[ rec_idx ].tag );
     503           0 :         rec_pool->ele[ rec_idx ].tag = 1;
     504           0 :         fd_funk_rec_query_t query[1];
     505           0 :         fd_funk_rec_t const * rec2 = fd_funk_rec_query_try_global( funk, &txn->xid, rec_pool->ele[ rec_idx ].pair.key, NULL, query );
     506           0 :         TEST( rec2 == rec_pool->ele + rec_idx );
     507           0 :         uint next_idx = rec_pool->ele[ rec_idx ].next_idx;
     508           0 :         if( !fd_funk_rec_idx_is_null( next_idx ) ) TEST( rec_pool->ele[ next_idx ].prev_idx==rec_idx );
     509           0 :         rec_idx = next_idx;
     510           0 :       }
     511           0 :     }
     512          12 :   } while(0);
     513             : 
     514          12 :   do {
     515          12 :     fd_funk_txn_all_iter_t txn_iter[1];
     516          12 :     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 ) ) {
     517           0 :       fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
     518             : 
     519           0 :       uint  rec_idx = txn->rec_tail_idx;
     520           0 :       while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     521           0 :         TEST( (rec_idx<rec_max) && rec_pool->ele[ rec_idx ].tag );
     522           0 :         rec_pool->ele[ rec_idx ].tag = 0;
     523           0 :         uint prev_idx = rec_pool->ele[ rec_idx ].prev_idx;
     524           0 :         if( !fd_funk_rec_idx_is_null( prev_idx ) ) TEST( rec_pool->ele[ prev_idx ].next_idx==rec_idx );
     525           0 :         rec_idx = prev_idx;
     526           0 :       }
     527           0 :     }
     528          12 :   } while(0);
     529             : 
     530          12 :   fd_funk_all_iter_t iter[1];
     531          12 :   for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     532           0 :     fd_funk_rec_t const * rec = fd_funk_all_iter_ele( iter );
     533           0 :     FD_TEST( rec->tag == fd_funk_txn_xid_eq_root( rec->pair.xid ) );
     534           0 :   }
     535             : 
     536          12 : # undef TEST
     537             : 
     538          12 :   return FD_FUNK_SUCCESS;
     539          12 : }

Generated by: LCOV version 1.14