LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_admin.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 257 287 89.5 %
Date: 2025-12-06 04:45:29 Functions: 16 16 100.0 %

          Line data    Source code
       1             : #include "fd_accdb_admin.h"
       2             : 
       3             : fd_accdb_admin_t *
       4             : fd_accdb_admin_join( fd_accdb_admin_t * ljoin,
       5          57 :                      void *             shfunk ) {
       6          57 :   if( FD_UNLIKELY( !ljoin ) ) {
       7           0 :     FD_LOG_WARNING(( "NULL ljoin" ));
       8           0 :     return NULL;
       9           0 :   }
      10          57 :   if( FD_UNLIKELY( !shfunk ) ) {
      11           0 :     FD_LOG_WARNING(( "NULL shfunk" ));
      12           0 :     return NULL;
      13           0 :   }
      14             : 
      15          57 :   memset( ljoin, 0, sizeof(fd_accdb_admin_t) );
      16          57 :   if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk ) ) ) {
      17           0 :     FD_LOG_CRIT(( "fd_funk_join failed" ));
      18           0 :   }
      19             : 
      20          57 :   return ljoin;
      21          57 : }
      22             : 
      23             : void *
      24             : fd_accdb_admin_leave( fd_accdb_admin_t * admin,
      25          54 :                       void **            opt_shfunk ) {
      26          54 :   if( FD_UNLIKELY( !admin ) ) FD_LOG_CRIT(( "NULL ljoin" ));
      27             : 
      28          54 :   if( FD_UNLIKELY( !fd_funk_leave( admin->funk, opt_shfunk ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
      29             : 
      30          54 :   return admin;
      31          54 : }
      32             : 
      33             : /* Begin transaction-level operations.  It is assumed that funk_txn data
      34             :    structures are not concurrently modified.  This includes txn_pool and
      35             :    txn_map. */
      36             : 
      37             : void
      38             : fd_accdb_attach_child( fd_accdb_admin_t *        db,
      39             :                        fd_funk_txn_xid_t const * xid_parent,
      40      571812 :                        fd_funk_txn_xid_t const * xid_new ) {
      41      571812 :   FD_LOG_INFO(( "accdb txn xid %lu:%lu: created with parent %lu:%lu",
      42      571812 :                 xid_new   ->ul[0], xid_new   ->ul[1],
      43      571812 :                 xid_parent->ul[0], xid_parent->ul[1] ));
      44      571812 :   fd_funk_txn_prepare( db->funk, xid_parent, xid_new );
      45      571812 : }
      46             : 
      47             : static void
      48             : fd_accdb_txn_cancel_one( fd_accdb_admin_t * admin,
      49      347094 :                          fd_funk_txn_t *    txn ) {
      50      347094 :   FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: cancel", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
      51             : 
      52      347094 :   if( FD_UNLIKELY( txn->state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
      53           0 :     FD_LOG_CRIT(( "cannot cancel xid %lu:%lu: unxpected state %u-%s",
      54           0 :                   txn->xid.ul[0], txn->xid.ul[1],
      55           0 :                   txn->state, fd_funk_txn_state_str( txn->state ) ));
      56           0 :   }
      57      347094 :   fd_funk_t * funk = admin->funk;
      58      347094 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( txn->child_head_cidx ) ||
      59      347094 :                    !fd_funk_txn_idx_is_null( txn->child_tail_cidx ) ) ) {
      60           0 :     FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: txn at %p with xid %lu:%lu has children (data corruption?)",
      61           0 :                   (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
      62           0 :   }
      63             : 
      64             :   /* Phase 1: Drain users from transaction */
      65             : 
      66      347094 :   fd_rwlock_write( txn->lock );
      67      347094 :   FD_COMPILER_MFENCE();
      68      347094 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_CANCEL;
      69             : 
      70             :   /* Phase 2: Detach all records */
      71             : 
      72      347094 :   FD_COMPILER_MFENCE();
      73      347094 :   uint const rec_head_idx = txn->rec_head_idx;
      74      347094 :   txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
      75      347094 :   txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
      76      347094 :   FD_COMPILER_MFENCE();
      77             : 
      78             :   /* Phase 3: Remove records */
      79             : 
      80      347094 :   ulong rec_cnt = 0UL;
      81      347094 :   uint rec_idx = rec_head_idx;
      82     1030707 :   while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
      83      683613 :     fd_funk_rec_t * rec = &funk->rec_pool->ele[ rec_idx ];
      84      683613 :     fd_funk_xid_key_pair_t pair = FD_VOLATILE_CONST( rec->pair );
      85             : 
      86      683613 :     uint next_idx = rec->next_idx;
      87      683613 :     if( FD_UNLIKELY( !fd_funk_txn_xid_eq( pair.xid, &txn->xid ) ) ) {
      88           0 :       FD_LOG_CRIT(( "Record does not belong to txn being cancelled (data corruption?): rec_idx=%u", rec_idx ));
      89           0 :     }
      90             : 
      91             :     /* Phase 3.1: Hide record */
      92             : 
      93      683613 :     fd_funk_rec_query_t query[1];
      94      683613 :     int remove_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
      95      683613 :     if( FD_UNLIKELY( remove_err ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
      96      683613 :     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 ));
      97             : 
      98             :     /* Phase 3.2: Mark record as invalid */
      99             : 
     100      683613 :     FD_COMPILER_MFENCE();
     101      683613 :     memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     102      683613 :     FD_COMPILER_MFENCE();
     103             : 
     104             :     /* Phase 3.3: Free record */
     105             : 
     106      683613 :     fd_funk_val_flush( rec, funk->alloc, funk->wksp );
     107      683613 :     rec->next_idx = FD_FUNK_REC_IDX_NULL;
     108      683613 :     rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     109      683613 :     fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
     110      683613 :     rec_idx = next_idx;
     111      683613 :     rec_cnt++;
     112      683613 :   }
     113      347094 :   FD_LOG_INFO(( "accdb freed %lu records while cancelling txn %lu:%lu",
     114      347094 :                 rec_cnt, txn->xid.ul[0], txn->xid.ul[1] ));
     115             : 
     116             :   /* Phase 4: Remove transaction from fork graph */
     117             : 
     118      347094 :   uint self_cidx = fd_funk_txn_cidx( (ulong)( txn-funk->txn_pool->ele ) );
     119      347094 :   uint prev_cidx = txn->sibling_prev_cidx; ulong prev_idx = fd_funk_txn_idx( prev_cidx );
     120      347094 :   uint next_cidx = txn->sibling_next_cidx; ulong next_idx = fd_funk_txn_idx( next_cidx );
     121      347094 :   if( !fd_funk_txn_idx_is_null( next_idx ) ) {
     122      156813 :     funk->txn_pool->ele[ next_idx ].sibling_prev_cidx = prev_cidx;
     123      156813 :   }
     124      347094 :   if( !fd_funk_txn_idx_is_null( prev_idx ) ) {
     125      109587 :     funk->txn_pool->ele[ prev_idx ].sibling_next_cidx = next_cidx;
     126      109587 :   }
     127      347094 :   if( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) {
     128      169809 :     fd_funk_txn_t * parent = &funk->txn_pool->ele[ fd_funk_txn_idx( txn->parent_cidx ) ];
     129      169809 :     if( parent->child_head_cidx==self_cidx ) parent->child_head_cidx = next_cidx;
     130      169809 :     if( parent->child_tail_cidx==self_cidx ) parent->child_tail_cidx = prev_cidx;
     131      177285 :   } else {
     132      177285 :     if( funk->shmem->child_head_cidx==self_cidx ) funk->shmem->child_head_cidx = next_cidx;
     133      177285 :     if( funk->shmem->child_tail_cidx==self_cidx ) funk->shmem->child_tail_cidx = prev_cidx;
     134      177285 :   }
     135             : 
     136             :   /* Phase 5: Remove transcation from index */
     137             : 
     138      347094 :   fd_funk_txn_map_query_t query[1];
     139      347094 :   int remove_err = fd_funk_txn_map_remove( funk->txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
     140      347094 :   if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
     141           0 :     FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: fd_funk_txn_map_remove(%lu:%lu) failed: %i-%s",
     142           0 :                   txn->xid.ul[0], txn->xid.ul[1], remove_err, fd_map_strerror( remove_err ) ));
     143           0 :   }
     144             : 
     145             :   /* Phase 6: Free transaction object */
     146             : 
     147      347094 :   txn->parent_cidx       = UINT_MAX;
     148      347094 :   txn->sibling_prev_cidx = UINT_MAX;
     149      347094 :   txn->sibling_next_cidx = UINT_MAX;
     150      347094 :   fd_rwlock_unwrite( txn->lock );
     151      347094 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
     152      347094 :   fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     153      347094 : }
     154             : 
     155             : /* Cancels txn and all children */
     156             : 
     157             : static void
     158             : fd_accdb_txn_cancel_tree( fd_accdb_admin_t * accdb,
     159      347094 :                           fd_funk_txn_t *    txn ) {
     160      516891 :   for(;;) {
     161      516891 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     162      516891 :     if( fd_funk_txn_idx_is_null( child_idx ) ) break;
     163      169797 :     fd_funk_txn_t * child = &accdb->funk->txn_pool->ele[ child_idx ];
     164      169797 :     fd_accdb_txn_cancel_tree( accdb, child );
     165      169797 :   }
     166      347094 :   fd_accdb_txn_cancel_one( accdb, txn );
     167      347094 : }
     168             : 
     169             : /* Cancels all left/right siblings */
     170             : 
     171             : static void
     172             : fd_accdb_txn_cancel_prev_list( fd_accdb_admin_t * accdb,
     173      224694 :                                fd_funk_txn_t *    txn ) {
     174      224694 :   ulong self_idx = (ulong)( txn - accdb->funk->txn_pool->ele );
     175      313761 :   for(;;) {
     176      313761 :     ulong prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
     177      313761 :     if( FD_UNLIKELY( prev_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
     178      313761 :     if( fd_funk_txn_idx_is_null( prev_idx ) ) break;
     179       89067 :     fd_funk_txn_t * sibling = &accdb->funk->txn_pool->ele[ prev_idx ];
     180       89067 :     fd_accdb_txn_cancel_tree( accdb, sibling );
     181       89067 :   }
     182      224694 : }
     183             : 
     184             : static void
     185             : fd_accdb_txn_cancel_next_list( fd_accdb_admin_t * accdb,
     186      224736 :                                fd_funk_txn_t *    txn ) {
     187      224736 :   ulong self_idx = (ulong)( txn - accdb->funk->txn_pool->ele );
     188      312924 :   for(;;) {
     189      312924 :     ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
     190      312924 :     if( FD_UNLIKELY( next_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
     191      312924 :     if( fd_funk_txn_idx_is_null( next_idx ) ) break;
     192       88188 :     fd_funk_txn_t * sibling = &accdb->funk->txn_pool->ele[ next_idx ];
     193       88188 :     fd_accdb_txn_cancel_tree( accdb, sibling );
     194       88188 :   }
     195      224736 : }
     196             : 
     197             : void
     198             : fd_accdb_cancel( fd_accdb_admin_t *        accdb,
     199          42 :                  fd_funk_txn_xid_t const * xid ) {
     200          42 :   fd_funk_t * funk = accdb->funk;
     201             : 
     202          42 :   fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
     203          42 :   if( FD_UNLIKELY( !txn ) ) {
     204           0 :     FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
     205           0 :   }
     206             : 
     207          42 :   fd_accdb_txn_cancel_next_list( accdb, txn );
     208          42 :   fd_accdb_txn_cancel_tree( accdb, txn );
     209          42 : }
     210             : 
     211             : /* fd_accdb_chain_gc_root cleans up a stale "rooted" version of a
     212             :    record. */
     213             : 
     214             : static void
     215             : fd_accdb_chain_gc_root( fd_accdb_admin_t *             accdb,
     216      593349 :                         fd_funk_xid_key_pair_t const * pair ) {
     217      593349 :   fd_funk_t * funk = accdb->funk;
     218             : 
     219             :   /* Phase 1: Remove record from map if found */
     220             : 
     221      593349 :   fd_funk_rec_query_t query[1];
     222      593349 :   int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     223      593349 :   if( rm_err==FD_MAP_ERR_KEY ) return;
     224      593148 :   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 ) ));
     225             : 
     226             :   /* Phase 2: Invalidate record */
     227             : 
     228      593148 :   fd_funk_rec_t * old_rec = query->ele;
     229      593148 :   memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     230      593148 :   FD_COMPILER_MFENCE();
     231             : 
     232             :   /* Phase 3: Free record */
     233             : 
     234      593148 :   old_rec->map_next = FD_FUNK_REC_IDX_NULL;
     235      593148 :   fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
     236      593148 :   fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
     237      593148 : }
     238             : 
     239             : /* fd_accdb_publish_recs moves all records in a transaction to the DB
     240             :    root.  Currently, the DB root is stored by funk, which might change
     241             :    in the future.
     242             : 
     243             :    It is assumed at this point that the txn has no more concurrent
     244             :    users. */
     245             : 
     246             : static void
     247             : fd_accdb_publish_recs( fd_accdb_admin_t * accdb,
     248      224694 :                        fd_funk_txn_t *    txn ) {
     249             :   /* Iterate record list */
     250      224694 :   uint head = txn->rec_head_idx;
     251      224694 :   txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
     252      224694 :   txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     253      818043 :   while( !fd_funk_rec_idx_is_null( head ) ) {
     254      593349 :     fd_funk_rec_t * rec = &accdb->funk->rec_pool->ele[ head ];
     255             : 
     256             :     /* Evict previous value from hash chain */
     257      593349 :     fd_funk_xid_key_pair_t pair[1];
     258      593349 :     fd_funk_rec_key_copy( pair->key, rec->pair.key );
     259      593349 :     fd_funk_txn_xid_set_root( pair->xid );
     260      593349 :     fd_accdb_chain_gc_root( accdb, pair );
     261             : 
     262             :     /* Migrate record to root */
     263      593349 :     uint next = rec->next_idx;
     264      593349 :     rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     265      593349 :     rec->next_idx = FD_FUNK_REC_IDX_NULL;
     266      593349 :     fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } };
     267      593349 :     fd_funk_txn_xid_st_atomic( rec->pair.xid, &root );
     268             : 
     269      593349 :     head = next; /* next record */
     270      593349 :   }
     271      224694 : }
     272             : 
     273             : /* fd_accdb_txn_publish_one merges an in-prep transaction whose
     274             :    parent is the last published, into the parent. */
     275             : 
     276             : static void
     277             : fd_accdb_txn_publish_one( fd_accdb_admin_t * accdb,
     278      224694 :                           fd_funk_txn_t *    txn ) {
     279      224694 :   fd_funk_t * funk = accdb->funk;
     280             : 
     281             :   /* Phase 1: Mark transaction as "last published" */
     282             : 
     283      224694 :   fd_funk_txn_xid_t xid[1]; fd_funk_txn_xid_copy( xid, fd_funk_txn_xid( txn ) );
     284      224694 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
     285           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] ));
     286           0 :   }
     287      224694 :   fd_funk_txn_xid_st_atomic( funk->shmem->last_publish, xid );
     288      224694 :   FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: publish", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
     289             : 
     290             :   /* Phase 2: Drain users from transaction */
     291             : 
     292      224694 :   fd_rwlock_write( txn->lock );
     293      224694 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_PUBLISH;
     294             : 
     295             :   /* Phase 3: Migrate records */
     296             : 
     297      224694 :   fd_accdb_publish_recs( accdb, txn );
     298             : 
     299             :   /* Phase 4: Remove transaction from fork graph
     300             : 
     301             :      Because the transaction has no more records, removing it from the
     302             :      fork graph has no visible side effects to concurrent query ops
     303             :      (always return "no found") or insert ops (refuse to write to a
     304             :      "publish" state txn). */
     305             : 
     306      224694 :   { /* Adjust the parent pointers of the children to point to "last published" */
     307      224694 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     308      372246 :     while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
     309      147552 :       funk->txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     310      147552 :       child_idx = fd_funk_txn_idx( funk->txn_pool->ele[ child_idx ].sibling_next_cidx );
     311      147552 :     }
     312      224694 :   }
     313             : 
     314             :   /* Phase 5: Remove transaction from index
     315             : 
     316             :      The transaction is now an orphan and won't get any new records. */
     317             : 
     318      224694 :   fd_funk_txn_map_query_t query[1];
     319      224694 :   int remove_err = fd_funk_txn_map_remove( funk->txn_map, xid, NULL, query, 0 );
     320      224694 :   if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
     321           0 :     FD_LOG_CRIT(( "fd_accdb_publish failed: fd_funk_txn_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
     322           0 :   }
     323             : 
     324             :   /* Phase 6: Free transaction object */
     325             : 
     326      224694 :   fd_rwlock_unwrite( txn->lock );
     327      224694 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
     328      224694 :   txn->parent_cidx       = UINT_MAX;
     329      224694 :   txn->sibling_prev_cidx = UINT_MAX;
     330      224694 :   txn->sibling_next_cidx = UINT_MAX;
     331      224694 :   txn->child_head_cidx   = UINT_MAX;
     332      224694 :   txn->child_tail_cidx   = UINT_MAX;
     333      224694 :   fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     334      224694 : }
     335             : 
     336             : void
     337             : fd_accdb_advance_root( fd_accdb_admin_t *        accdb,
     338      224694 :                        fd_funk_txn_xid_t const * xid ) {
     339      224694 :   fd_funk_t * funk = accdb->funk;
     340             : 
     341      224694 :   fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
     342      224694 :   if( FD_UNLIKELY( !txn ) ) {
     343           0 :     FD_LOG_CRIT(( "fd_accdb_txn_advance_root failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
     344           0 :   }
     345             : 
     346      224694 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
     347           0 :     FD_LOG_CRIT(( "fd_accdb_txn_advance_root: parent of txn %lu:%lu is not root", xid->ul[0], xid->ul[1] ));
     348           0 :   }
     349             : 
     350      224694 :   FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: advancing root",
     351      224694 :                 (void *)txn,
     352      224694 :                 xid->ul[0], xid->ul[1] ));
     353             : 
     354      224694 :   fd_accdb_txn_cancel_prev_list( accdb, txn );
     355      224694 :   fd_accdb_txn_cancel_next_list( accdb, txn );
     356      224694 :   txn->sibling_prev_cidx = UINT_MAX;
     357      224694 :   txn->sibling_next_cidx = UINT_MAX;
     358             : 
     359             :   /* Children of transaction are now children of root */
     360      224694 :   funk->shmem->child_head_cidx = txn->child_head_cidx;
     361      224694 :   funk->shmem->child_tail_cidx = txn->child_tail_cidx;
     362             : 
     363      224694 :   fd_accdb_txn_publish_one( accdb, txn );
     364      224694 : }
     365             : 
     366             : /* reset_rec_map frees all records in a funk instance. */
     367             : 
     368             : static void
     369           3 : reset_rec_map( fd_funk_t * funk ) {
     370           3 :   fd_wksp_t *          wksp     = funk->wksp;
     371           3 :   fd_alloc_t *         alloc    = funk->alloc;
     372           3 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     373           3 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     374             : 
     375           3 :   ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
     376         771 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     377         768 :     for(
     378         768 :         fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
     379        1002 :         !fd_funk_rec_map_iter_done( iter );
     380         768 :     ) {
     381         234 :       fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( iter );
     382         234 :       ulong next = fd_funk_rec_map_private_idx( rec->map_next );;
     383             : 
     384             :       /* Remove rec object from map */
     385         234 :       fd_funk_rec_map_query_t rec_query[1];
     386         234 :       int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     387         234 :       fd_funk_rec_key_t key; fd_funk_rec_key_copy( &key, rec->pair.key );
     388         234 :       if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
     389             : 
     390             :       /* Free rec resources */
     391         234 :       rec->map_next = FD_FUNK_REC_IDX_NULL;
     392         234 :       rec->next_idx = FD_FUNK_REC_IDX_NULL;
     393         234 :       rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     394         234 :       memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     395         234 :       fd_funk_val_flush( rec, alloc, wksp );
     396         234 :       fd_funk_rec_pool_release( rec_pool, rec, 1 );
     397         234 :       iter.ele_idx = next;
     398         234 :     }
     399         768 :   }
     400           3 : }
     401             : 
     402             : /* clear_txn_list does a depth-first traversal of the txn tree.
     403             :    Removes all txns. */
     404             : 
     405             : static void
     406             : clear_txn_list( fd_funk_t * funk,
     407          15 :                 ulong       txn_head_idx ) {
     408          15 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     409          15 :   fd_funk_txn_map_t *  txn_map  = funk->txn_map;
     410          15 :   for( ulong idx = txn_head_idx;
     411          27 :        !fd_funk_txn_idx_is_null( idx );
     412          15 :   ) {
     413          12 :     fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
     414          12 :     fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
     415          12 :     ulong next_idx  = fd_funk_txn_idx( txn->sibling_next_cidx );
     416          12 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     417          12 :     txn->rec_head_idx      = FD_FUNK_REC_IDX_NULL;
     418          12 :     txn->rec_tail_idx      = FD_FUNK_REC_IDX_NULL;
     419          12 :     txn->child_head_cidx   = UINT_MAX;
     420          12 :     txn->child_tail_cidx   = UINT_MAX;
     421          12 :     txn->parent_cidx       = UINT_MAX;
     422          12 :     txn->sibling_prev_cidx = UINT_MAX;
     423          12 :     txn->sibling_next_cidx = UINT_MAX;
     424          12 :     clear_txn_list( funk, child_idx );
     425          12 :     fd_funk_txn_map_query_t query[1];
     426          12 :     int rm_err = fd_funk_txn_map_remove( txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
     427          12 :     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 ) ));
     428          12 :     txn->state = FD_FUNK_TXN_STATE_FREE;
     429          12 :     int free_err = fd_funk_txn_pool_release( txn_pool, txn, 1 );
     430          12 :     if( FD_UNLIKELY( free_err!=FD_POOL_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_pool_release failed (%i)", free_err ));
     431          12 :     idx = next_idx;
     432          12 :   }
     433          15 :   funk->shmem->child_head_cidx = UINT_MAX;
     434          15 :   funk->shmem->child_tail_cidx = UINT_MAX;
     435          15 : }
     436             : 
     437             : void
     438           3 : fd_accdb_clear( fd_accdb_admin_t * cache ) {
     439           3 :   fd_funk_t * funk = cache->funk;
     440           3 :   clear_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
     441           3 :   reset_rec_map( funk );
     442           3 : }
     443             : 
     444             : void
     445         198 : fd_accdb_verify( fd_accdb_admin_t * admin ) {
     446         198 :   FD_TEST( fd_funk_verify( admin->funk )==FD_FUNK_SUCCESS );
     447         198 : }

Generated by: LCOV version 1.14