LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_admin_v1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 92 349 26.4 %
Date: 2026-03-19 18:19:27 Functions: 8 21 38.1 %

          Line data    Source code
       1             : #include "fd_accdb_admin_v1.h"
       2             : #include "../fd_flamenco_base.h"
       3             : 
       4             : FD_STATIC_ASSERT( alignof(fd_accdb_admin_v1_t)<=alignof(fd_accdb_admin_t), layout );
       5             : FD_STATIC_ASSERT( sizeof (fd_accdb_admin_v1_t)<=sizeof(fd_accdb_admin_t),  layout );
       6             : 
       7             : fd_accdb_admin_t *
       8             : fd_accdb_admin_v1_init( fd_accdb_admin_t * admin_,
       9             :                         void *             shfunk,
      10          12 :                         void *             shlocks ) {
      11          12 :   if( FD_UNLIKELY( !admin_ ) ) {
      12           0 :     FD_LOG_WARNING(( "NULL ljoin" ));
      13           0 :     return NULL;
      14           0 :   }
      15             : 
      16          12 :   fd_accdb_admin_v1_t * admin = (fd_accdb_admin_v1_t *)admin_;
      17          12 :   memset( admin, 0, sizeof(fd_accdb_admin_t) );
      18          12 :   admin->base.accdb_type = FD_ACCDB_TYPE_V1;
      19          12 :   admin->base.vt         = &fd_accdb_admin_v1_vt;
      20             : 
      21          12 :   if( FD_UNLIKELY( !fd_funk_join( admin->funk, shfunk, shlocks ) ) ) {
      22           0 :     FD_LOG_CRIT(( "fd_funk_join failed" ));
      23           0 :   }
      24             : 
      25          12 :   return admin_;
      26          12 : }
      27             : 
      28             : static fd_accdb_admin_v1_t *
      29      109514 : downcast( fd_accdb_admin_t * admin ) {
      30      109514 :   if( FD_UNLIKELY( !admin ) ) {
      31           0 :     FD_LOG_CRIT(( "NULL admin" ));
      32           0 :   }
      33      109514 :   if( FD_UNLIKELY( admin->base.accdb_type!=FD_ACCDB_TYPE_V1 ) ) {
      34           0 :     FD_LOG_CRIT(( "corrupt accdb_admin handle" ));
      35           0 :   }
      36      109514 :   return (fd_accdb_admin_v1_t *)admin;
      37      109514 : }
      38             : 
      39             : void
      40          12 : fd_accdb_admin_v1_fini( fd_accdb_admin_t * admin_ ) {
      41          12 :   fd_accdb_admin_v1_t * admin = downcast( admin_ );
      42          12 :   if( FD_UNLIKELY( !fd_funk_leave( admin->funk, NULL, NULL ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
      43          12 :   memset( admin, 0, sizeof(fd_accdb_admin_base_t) );
      44          12 : }
      45             : 
      46             : fd_funk_t *
      47       24607 : fd_accdb_admin_v1_funk( fd_accdb_admin_t * admin ) {
      48       24607 :   fd_accdb_admin_v1_t * a = downcast( admin );
      49       24607 :   return a->funk;
      50       24607 : }
      51             : 
      52             : fd_funk_txn_xid_t
      53           0 : fd_accdb_v1_root_get( fd_accdb_admin_t const * admin_ ) {
      54           0 :   fd_accdb_admin_v1_t const * admin = (fd_accdb_admin_v1_t const *)admin_;
      55           0 :   return *fd_funk_last_publish( admin->funk );
      56           0 : }
      57             : 
      58             : /* Begin transaction-level operations.  It is assumed that funk_txn data
      59             :    structures are not concurrently modified.  This includes txn_pool and
      60             :    txn_map. */
      61             : 
      62             : void
      63             : fd_accdb_v1_attach_child( fd_accdb_admin_t *        db_,
      64             :                           fd_funk_txn_xid_t const * xid_parent,
      65       54860 :                           fd_funk_txn_xid_t const * xid_new ) {
      66       54860 :   fd_accdb_admin_v1_t * db = downcast( db_ );
      67       54860 :   FD_LOG_INFO(( "accdb txn xid %lu:%lu: created with parent %lu:%lu",
      68       54860 :                 xid_new   ->ul[0], xid_new   ->ul[1],
      69       54860 :                 xid_parent->ul[0], xid_parent->ul[1] ));
      70       54860 :   fd_funk_txn_prepare( db->funk, xid_parent, xid_new );
      71       54860 : }
      72             : 
      73             : static void
      74             : fd_accdb_txn_cancel_one( fd_accdb_admin_v1_t * admin,
      75           0 :                          fd_funk_txn_t *       txn ) {
      76           0 :   FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: cancel", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
      77             : 
      78           0 :   if( FD_UNLIKELY( txn->state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
      79           0 :     FD_LOG_CRIT(( "cannot cancel xid %lu:%lu: unxpected state %u-%s",
      80           0 :                   txn->xid.ul[0], txn->xid.ul[1],
      81           0 :                   txn->state, fd_funk_txn_state_str( txn->state ) ));
      82           0 :   }
      83           0 :   fd_funk_t * funk = admin->funk;
      84           0 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( txn->child_head_cidx ) ||
      85           0 :                    !fd_funk_txn_idx_is_null( txn->child_tail_cidx ) ) ) {
      86           0 :     FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: txn at %p with xid %lu:%lu has children (data corruption?)",
      87           0 :                   (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
      88           0 :   }
      89             : 
      90             :   /* Phase 1: Drain users from transaction */
      91             : 
      92           0 :   ulong txn_idx = (ulong)( txn - funk->txn_pool->ele );
      93           0 :   fd_rwlock_write( &funk->txn_lock[ txn_idx ] );
      94           0 :   FD_COMPILER_MFENCE();
      95           0 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_CANCEL;
      96             : 
      97             :   /* Phase 2: Detach all records */
      98             : 
      99           0 :   FD_COMPILER_MFENCE();
     100           0 :   uint const rec_head_idx = txn->rec_head_idx;
     101           0 :   txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
     102           0 :   txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     103           0 :   FD_COMPILER_MFENCE();
     104             : 
     105             :   /* Phase 3: Remove records */
     106             : 
     107           0 :   ulong rec_cnt = 0UL;
     108           0 :   uint rec_idx = rec_head_idx;
     109           0 :   while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
     110           0 :     fd_funk_rec_t *        rec  = &funk->rec_pool->ele[ rec_idx ];
     111           0 :     fd_funk_xid_key_pair_t pair = FD_VOLATILE_CONST( rec->pair );
     112             : 
     113           0 :     uint next_idx = rec->next_idx;
     114           0 :     if( FD_UNLIKELY( !fd_funk_txn_xid_eq( pair.xid, &txn->xid ) ) ) {
     115           0 :       FD_LOG_CRIT(( "Record does not belong to txn being cancelled (data corruption?): rec_idx=%u", rec_idx ));
     116           0 :     }
     117             : 
     118             :     /* Phase 3.1: Hide record */
     119             : 
     120           0 :     fd_funk_rec_query_t query[1];
     121           0 :     int remove_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     122           0 :     if( FD_UNLIKELY( remove_err ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
     123           0 :     if( FD_UNLIKELY( query->ele!=rec ) ) FD_LOG_CRIT(( "Found duplicate record in map idx[0]=%p idx[1]=%p", (void *)query->ele, (void *)rec ));
     124             : 
     125             :     /* Phase 3.2: Mark record as invalid */
     126             : 
     127           0 :     FD_COMPILER_MFENCE();
     128           0 :     memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     129           0 :     FD_COMPILER_MFENCE();
     130             : 
     131             :     /* Phase 3.3: Free record */
     132             : 
     133           0 :     fd_funk_val_flush( rec, funk->alloc, funk->wksp );
     134           0 :     rec->next_idx = FD_FUNK_REC_IDX_NULL;
     135           0 :     rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     136           0 :     funk->rec_lock[ rec_idx ] = fd_funk_rec_ver_lock( fd_funk_rec_ver_inc( fd_funk_rec_ver_bits( funk->rec_lock[ rec_idx ] ) ), 0UL );
     137           0 :     fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
     138           0 :     rec_idx = next_idx;
     139           0 :     rec_cnt++;
     140           0 :   }
     141           0 :   admin->base.revert_cnt += rec_cnt;
     142           0 :   FD_LOG_INFO(( "accdb freed %lu records while cancelling txn %lu:%lu",
     143           0 :                 rec_cnt, txn->xid.ul[0], txn->xid.ul[1] ));
     144             : 
     145             :   /* Phase 4: Remove transaction from fork graph */
     146             : 
     147           0 :   uint self_cidx = fd_funk_txn_cidx( (ulong)( txn-funk->txn_pool->ele ) );
     148           0 :   uint prev_cidx = txn->sibling_prev_cidx; ulong prev_idx = fd_funk_txn_idx( prev_cidx );
     149           0 :   uint next_cidx = txn->sibling_next_cidx; ulong next_idx = fd_funk_txn_idx( next_cidx );
     150           0 :   if( !fd_funk_txn_idx_is_null( next_idx ) ) {
     151           0 :     funk->txn_pool->ele[ next_idx ].sibling_prev_cidx = prev_cidx;
     152           0 :   }
     153           0 :   if( !fd_funk_txn_idx_is_null( prev_idx ) ) {
     154           0 :     funk->txn_pool->ele[ prev_idx ].sibling_next_cidx = next_cidx;
     155           0 :   }
     156           0 :   if( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) {
     157           0 :     fd_funk_txn_t * parent = &funk->txn_pool->ele[ fd_funk_txn_idx( txn->parent_cidx ) ];
     158           0 :     if( parent->child_head_cidx==self_cidx ) parent->child_head_cidx = next_cidx;
     159           0 :     if( parent->child_tail_cidx==self_cidx ) parent->child_tail_cidx = prev_cidx;
     160           0 :   } else {
     161           0 :     if( funk->shmem->child_head_cidx==self_cidx ) funk->shmem->child_head_cidx = next_cidx;
     162           0 :     if( funk->shmem->child_tail_cidx==self_cidx ) funk->shmem->child_tail_cidx = prev_cidx;
     163           0 :   }
     164             : 
     165             :   /* Phase 5: Remove transaction from index */
     166             : 
     167           0 :   fd_funk_txn_map_query_t query[1];
     168           0 :   int remove_err = fd_funk_txn_map_remove( funk->txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
     169           0 :   if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
     170           0 :     FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: fd_funk_txn_map_remove(%lu:%lu) failed: %i-%s",
     171           0 :                   txn->xid.ul[0], txn->xid.ul[1], remove_err, fd_map_strerror( remove_err ) ));
     172           0 :   }
     173             : 
     174             :   /* Phase 6: Free transaction object */
     175             : 
     176           0 :   txn->parent_cidx       = UINT_MAX;
     177           0 :   txn->sibling_prev_cidx = UINT_MAX;
     178           0 :   txn->sibling_next_cidx = UINT_MAX;
     179           0 :   fd_rwlock_unwrite( &funk->txn_lock[ txn_idx ] );
     180           0 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
     181           0 :   fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     182           0 : }
     183             : 
     184             : /* Cancels txn and all children */
     185             : 
     186             : static void
     187             : fd_accdb_txn_cancel_tree( fd_accdb_admin_v1_t * accdb,
     188           0 :                           fd_funk_txn_t *       txn ) {
     189           0 :   for(;;) {
     190           0 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     191           0 :     if( fd_funk_txn_idx_is_null( child_idx ) ) break;
     192           0 :     fd_funk_txn_t * child = &accdb->funk->txn_pool->ele[ child_idx ];
     193           0 :     fd_accdb_txn_cancel_tree( accdb, child );
     194           0 :   }
     195           0 :   fd_accdb_txn_cancel_one( accdb, txn );
     196           0 : }
     197             : 
     198             : /* Cancels all left/right siblings */
     199             : 
     200             : static void
     201             : fd_accdb_txn_cancel_prev_list( fd_accdb_admin_v1_t * accdb,
     202           0 :                                fd_funk_txn_t *       txn ) {
     203           0 :   ulong self_idx = (ulong)( txn - accdb->funk->txn_pool->ele );
     204           0 :   for(;;) {
     205           0 :     ulong prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
     206           0 :     if( FD_UNLIKELY( prev_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
     207           0 :     if( fd_funk_txn_idx_is_null( prev_idx ) ) break;
     208           0 :     fd_funk_txn_t * sibling = &accdb->funk->txn_pool->ele[ prev_idx ];
     209           0 :     fd_accdb_txn_cancel_tree( accdb, sibling );
     210           0 :   }
     211           0 : }
     212             : 
     213             : static void
     214             : fd_accdb_txn_cancel_next_list( fd_accdb_admin_v1_t * accdb,
     215           0 :                                fd_funk_txn_t *       txn ) {
     216           0 :   ulong self_idx = (ulong)( txn - accdb->funk->txn_pool->ele );
     217           0 :   for(;;) {
     218           0 :     ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
     219           0 :     if( FD_UNLIKELY( next_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
     220           0 :     if( fd_funk_txn_idx_is_null( next_idx ) ) break;
     221           0 :     fd_funk_txn_t * sibling = &accdb->funk->txn_pool->ele[ next_idx ];
     222           0 :     fd_accdb_txn_cancel_tree( accdb, sibling );
     223           0 :   }
     224           0 : }
     225             : 
     226             : void
     227             : fd_accdb_txn_cancel_siblings( fd_accdb_admin_v1_t * accdb,
     228           0 :                               fd_funk_txn_t *       txn ) {
     229           0 :   fd_accdb_txn_cancel_prev_list( accdb, txn );
     230           0 :   fd_accdb_txn_cancel_next_list( accdb, txn );
     231           0 :   txn->sibling_prev_cidx = UINT_MAX;
     232           0 :   txn->sibling_next_cidx = UINT_MAX;
     233           0 : }
     234             : 
     235             : void
     236             : fd_accdb_v1_cancel( fd_accdb_admin_t *        accdb_,
     237           0 :                     fd_funk_txn_xid_t const * xid ) {
     238           0 :   fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
     239           0 :   fd_funk_t *           funk  = accdb->funk;
     240             : 
     241             :   /* Assume no concurrent access to txn_map */
     242             : 
     243           0 :   fd_funk_txn_map_query_t query[1];
     244           0 :   int query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 );
     245           0 :   if( FD_UNLIKELY( query_err ) ) {
     246           0 :     FD_LOG_CRIT(( "fd_accdb_cancel failed: fd_funk_txn_map_query_try(xid=%lu:%lu) returned (%i-%s)",
     247           0 :                    xid->ul[0], xid->ul[1], query_err, fd_map_strerror( query_err ) ));
     248           0 :   }
     249           0 :   fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( query );
     250             : 
     251           0 :   fd_accdb_txn_cancel_tree( accdb, txn );
     252           0 : }
     253             : 
     254             : /* fd_accdb_chain_reclaim "reclaims" a zero-lamport account by removing
     255             :    its underlying record. */
     256             : 
     257             : static void
     258             : fd_accdb_chain_reclaim( fd_accdb_admin_v1_t * accdb,
     259           0 :                         fd_funk_rec_t *       rec ) {
     260           0 :   fd_funk_t * funk = accdb->funk;
     261             : 
     262             :   /* Phase 1: Remove record from map */
     263             : 
     264           0 :   fd_funk_xid_key_pair_t pair = rec->pair;
     265           0 :   fd_funk_rec_query_t query[1];
     266           0 :   int rm_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     267           0 :   if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) ));
     268           0 :   FD_COMPILER_MFENCE();
     269             : 
     270             :   /* Phase 2: Invalidate record */
     271             : 
     272           0 :   fd_funk_rec_t * old_rec = query->ele;
     273           0 :   memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     274           0 :   FD_COMPILER_MFENCE();
     275             : 
     276             :   /* Phase 3: Free record */
     277             : 
     278           0 :   old_rec->map_next = FD_FUNK_REC_IDX_NULL;
     279           0 :   fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
     280           0 :   fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
     281           0 :   accdb->base.reclaim_cnt++;
     282           0 : }
     283             : 
     284             : /* fd_accdb_chain_gc_root cleans up a stale "rooted" version of a
     285             :    record. */
     286             : 
     287             : static void
     288             : fd_accdb_chain_gc_root( fd_accdb_admin_v1_t *          accdb,
     289           0 :                         fd_funk_xid_key_pair_t const * pair ) {
     290           0 :   fd_funk_t * funk = accdb->funk;
     291             : 
     292             :   /* Phase 1: Remove record from map if found */
     293             : 
     294           0 :   fd_funk_rec_query_t query[1];
     295           0 :   int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     296           0 :   if( rm_err==FD_MAP_ERR_KEY ) return;
     297           0 :   if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) ));
     298           0 :   FD_COMPILER_MFENCE();
     299             : 
     300             :   /* Phase 2: Invalidate record */
     301             : 
     302           0 :   fd_funk_rec_t * old_rec = query->ele;
     303           0 :   memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     304           0 :   FD_COMPILER_MFENCE();
     305             : 
     306             :   /* Phase 3: Free record */
     307             : 
     308           0 :   old_rec->map_next = FD_FUNK_REC_IDX_NULL;
     309           0 :   fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
     310           0 :   fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
     311           0 :   accdb->base.gc_root_cnt++;
     312           0 : }
     313             : 
     314             : /* fd_accdb_publish_recs moves all records in a transaction to the DB
     315             :    root.  Currently, the DB root is stored by funk, which might change
     316             :    in the future.
     317             : 
     318             :    It is assumed at this point that the txn has no more concurrent
     319             :    users. */
     320             : 
     321             : static void
     322             : fd_accdb_publish_recs( fd_accdb_admin_v1_t * accdb,
     323           0 :                        fd_funk_txn_t *       txn ) {
     324             :   /* Iterate record list */
     325           0 :   uint head = txn->rec_head_idx;
     326           0 :   txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
     327           0 :   txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     328           0 :   fd_wksp_t * funk_wksp = accdb->funk->wksp;
     329           0 :   while( !fd_funk_rec_idx_is_null( head ) ) {
     330           0 :     fd_funk_rec_t * rec = &accdb->funk->rec_pool->ele[ head ];
     331             : 
     332             :     /* Evict previous value from hash chain */
     333           0 :     fd_funk_xid_key_pair_t pair[1];
     334           0 :     fd_funk_rec_key_copy( pair->key, rec->pair.key );
     335           0 :     fd_funk_txn_xid_set_root( pair->xid );
     336           0 :     fd_accdb_chain_gc_root( accdb, pair );
     337             : 
     338             :     /* Root or reclaim record */
     339           0 :     uint next = rec->next_idx;
     340           0 :     fd_account_meta_t const * meta = fd_funk_val( rec, funk_wksp );
     341           0 :     FD_CRIT( meta && rec->val_sz>=sizeof(fd_account_meta_t), "invalid funk record value" );
     342           0 :     if( !meta->lamports ) {
     343             :       /* Remove record */
     344           0 :       fd_accdb_chain_reclaim( accdb, rec );
     345           0 :     } else {
     346             :       /* Migrate record to root */
     347           0 :       rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     348           0 :       rec->next_idx = FD_FUNK_REC_IDX_NULL;
     349           0 :       fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } };
     350           0 :       fd_funk_txn_xid_st_atomic( rec->pair.xid, &root );
     351           0 :       accdb->base.root_cnt++;
     352           0 :       accdb->base.root_tot_sz += rec->val_sz;
     353           0 :     }
     354             : 
     355           0 :     head = next; /* next record */
     356           0 :   }
     357           0 : }
     358             : 
     359             : /* fd_accdb_txn_publish_one merges an in-prep transaction whose
     360             :    parent is the last published, into the parent. */
     361             : 
     362             : static void
     363             : fd_accdb_txn_publish_one( fd_accdb_admin_v1_t * accdb,
     364           0 :                           fd_funk_txn_t *       txn ) {
     365           0 :   fd_funk_t * funk = accdb->funk;
     366             : 
     367             :   /* Phase 1: Mark transaction as "last published" */
     368             : 
     369           0 :   fd_funk_txn_xid_t xid[1]; fd_funk_txn_xid_copy( xid, fd_funk_txn_xid( txn ) );
     370           0 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
     371           0 :     FD_LOG_CRIT(( "fd_accdb_publish failed: txn with xid %lu:%lu is not a child of the last published txn", xid->ul[0], xid->ul[1] ));
     372           0 :   }
     373           0 :   fd_funk_txn_xid_st_atomic( funk->shmem->last_publish, xid );
     374           0 :   FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: publish", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
     375             : 
     376             :   /* Phase 2: Drain users from transaction */
     377             : 
     378           0 :   ulong txn_idx = (ulong)( txn - funk->txn_pool->ele );
     379           0 :   fd_rwlock_write( &funk->txn_lock[ txn_idx ] );
     380           0 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_PUBLISH;
     381             : 
     382             :   /* Phase 3: Migrate records */
     383             : 
     384           0 :   fd_accdb_publish_recs( accdb, txn );
     385             : 
     386             :   /* Phase 4: Remove transaction from fork graph
     387             : 
     388             :      Because the transaction has no more records, removing it from the
     389             :      fork graph has no visible side effects to concurrent query ops
     390             :      (always return "no found") or insert ops (refuse to write to a
     391             :      "publish" state txn). */
     392             : 
     393           0 :   { /* Adjust the parent pointers of the children to point to "last published" */
     394           0 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     395           0 :     while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
     396           0 :       funk->txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     397           0 :       child_idx = fd_funk_txn_idx( funk->txn_pool->ele[ child_idx ].sibling_next_cidx );
     398           0 :     }
     399           0 :   }
     400             : 
     401             :   /* Phase 5: Remove transaction from index
     402             : 
     403             :      The transaction is now an orphan and won't get any new records. */
     404             : 
     405           0 :   fd_funk_txn_map_query_t query[1];
     406           0 :   int remove_err = fd_funk_txn_map_remove( funk->txn_map, xid, NULL, query, 0 );
     407           0 :   if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
     408           0 :     FD_LOG_CRIT(( "fd_accdb_publish failed: fd_funk_txn_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
     409           0 :   }
     410             : 
     411             :   /* Phase 6: Free transaction object */
     412             : 
     413           0 :   fd_rwlock_unwrite( &funk->txn_lock[ txn_idx ] );
     414           0 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
     415           0 :   txn->parent_cidx       = UINT_MAX;
     416           0 :   txn->sibling_prev_cidx = UINT_MAX;
     417           0 :   txn->sibling_next_cidx = UINT_MAX;
     418           0 :   txn->child_head_cidx   = UINT_MAX;
     419           0 :   txn->child_tail_cidx   = UINT_MAX;
     420           0 :   fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     421           0 : }
     422             : 
     423             : void
     424             : fd_accdb_v1_advance_root( fd_accdb_admin_t *        accdb_,
     425           0 :                           fd_funk_txn_xid_t const * xid ) {
     426           0 :   fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
     427           0 :   fd_funk_t *           funk  = accdb->funk;
     428             : 
     429             :   /* Assume no concurrent access to txn_map */
     430             : 
     431           0 :   long dt = -fd_tickcount();
     432           0 :   fd_funk_txn_map_query_t query[1];
     433           0 :   int query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 );
     434           0 :   if( FD_UNLIKELY( query_err ) ) {
     435           0 :     FD_LOG_CRIT(( "fd_accdb_advance_root failed: fd_funk_txn_map_query_try(xid=%lu:%lu) returned (%i-%s)",
     436           0 :                    xid->ul[0], xid->ul[1], query_err, fd_map_strerror( query_err ) ));
     437           0 :   }
     438           0 :   fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( query );
     439             : 
     440           0 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
     441           0 :     FD_LOG_CRIT(( "fd_accdb_txn_advance_root: parent of txn %lu:%lu is not root", xid->ul[0], xid->ul[1] ));
     442           0 :   }
     443             : 
     444           0 :   FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: advancing root",
     445           0 :                 (void *)txn,
     446           0 :                 xid->ul[0], xid->ul[1] ));
     447             : 
     448           0 :   fd_accdb_txn_cancel_siblings( accdb, txn );
     449             : 
     450             :   /* Children of transaction are now children of root */
     451           0 :   funk->shmem->child_head_cidx = txn->child_head_cidx;
     452           0 :   funk->shmem->child_tail_cidx = txn->child_tail_cidx;
     453             : 
     454           0 :   fd_accdb_txn_publish_one( accdb, txn );
     455             : 
     456           0 :   dt += fd_tickcount();
     457           0 :   accdb->base.dt_gc += dt;
     458           0 : }
     459             : 
     460             : /* reset_rec_map frees all records in a funk instance. */
     461             : 
     462             : static void
     463       30112 : reset_rec_map( fd_funk_t * funk ) {
     464       30112 :   fd_wksp_t *          wksp     = funk->wksp;
     465       30112 :   fd_alloc_t *         alloc    = funk->alloc;
     466       30112 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     467       30112 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     468             : 
     469       30112 :   ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
     470    15304807 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     471    15274695 :     for(
     472    15274695 :         fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
     473    15405118 :         !fd_funk_rec_map_iter_done( iter );
     474    15274695 :     ) {
     475      130423 :       fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( iter );
     476      130423 :       ulong next = fd_funk_rec_map_private_idx( rec->map_next );;
     477             : 
     478             :       /* Remove rec object from map */
     479      130423 :       fd_funk_rec_map_query_t rec_query[1];
     480      130423 :       int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     481      130423 :       fd_funk_rec_key_t key; fd_funk_rec_key_copy( &key, rec->pair.key );
     482      130423 :       if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
     483             : 
     484             :       /* Free rec resources */
     485      130423 :       rec->map_next = FD_FUNK_REC_IDX_NULL;
     486      130423 :       rec->next_idx = FD_FUNK_REC_IDX_NULL;
     487      130423 :       rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     488      130423 :       memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     489      130423 :       fd_funk_val_flush( rec, alloc, wksp );
     490      130423 :       fd_funk_rec_pool_release( rec_pool, rec, 1 );
     491      130423 :       iter.ele_idx = next;
     492      130423 :     }
     493    15274695 :   }
     494       30112 : }
     495             : 
     496             : /* clear_txn_list does a depth-first traversal of the txn tree.
     497             :    Removes all txns. */
     498             : 
     499             : static void
     500             : clear_txn_list( fd_funk_t * funk,
     501       85114 :                 ulong       txn_head_idx ) {
     502       85114 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     503       85114 :   fd_funk_txn_map_t *  txn_map  = funk->txn_map;
     504       85114 :   for( ulong idx = txn_head_idx;
     505      140118 :        !fd_funk_txn_idx_is_null( idx );
     506       85114 :   ) {
     507       55004 :     fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
     508       55004 :     fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
     509       55004 :     ulong next_idx  = fd_funk_txn_idx( txn->sibling_next_cidx );
     510       55004 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     511       55004 :     txn->rec_head_idx      = FD_FUNK_REC_IDX_NULL;
     512       55004 :     txn->rec_tail_idx      = FD_FUNK_REC_IDX_NULL;
     513       55004 :     txn->child_head_cidx   = UINT_MAX;
     514       55004 :     txn->child_tail_cidx   = UINT_MAX;
     515       55004 :     txn->parent_cidx       = UINT_MAX;
     516       55004 :     txn->sibling_prev_cidx = UINT_MAX;
     517       55004 :     txn->sibling_next_cidx = UINT_MAX;
     518       55004 :     clear_txn_list( funk, child_idx );
     519       55004 :     fd_funk_txn_map_query_t query[1];
     520       55004 :     int rm_err = fd_funk_txn_map_remove( txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
     521       55004 :     if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) ));
     522       55004 :     txn->state = FD_FUNK_TXN_STATE_FREE;
     523       55004 :     int free_err = fd_funk_txn_pool_release( txn_pool, txn, 1 );
     524       55004 :     if( FD_UNLIKELY( free_err!=FD_POOL_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_pool_release failed (%i)", free_err ));
     525       55004 :     idx = next_idx;
     526       55004 :   }
     527       85114 : }
     528             : 
     529             : void
     530       30135 : fd_accdb_v1_clear( fd_accdb_admin_t * accdb_ ) {
     531       30135 :   fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
     532       30135 :   fd_funk_t *           funk  = accdb->funk;
     533       30135 :   clear_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
     534       30135 :   funk->shmem->child_head_cidx = UINT_MAX;
     535       30135 :   funk->shmem->child_tail_cidx = UINT_MAX;
     536       30135 :   reset_rec_map( funk );
     537       30135 : }
     538             : 
     539             : void
     540           0 : fd_accdb_v1_verify( fd_accdb_admin_t * accdb_ ) {
     541           0 :   fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
     542           0 :   FD_TEST( fd_funk_verify( accdb->funk )==FD_FUNK_SUCCESS );
     543           0 : }
     544             : 
     545             : fd_accdb_admin_vt_t const fd_accdb_admin_v1_vt = {
     546             :   .fini          = fd_accdb_admin_v1_fini,
     547             :   .root_get      = fd_accdb_v1_root_get,
     548             :   .attach_child  = fd_accdb_v1_attach_child,
     549             :   .advance_root  = fd_accdb_v1_advance_root,
     550             :   .cancel        = fd_accdb_v1_cancel
     551             : };

Generated by: LCOV version 1.14