LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_reclaim.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 73 81 90.1 %
Date: 2026-06-29 05:51:35 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "fd_progcache_reclaim.h"
       2             : #include "fd_progcache_clock.h"
       3             : #include "fd_progcache_user.h"
       4             : #include "../../util/racesan/fd_racesan_target.h"
       5             : 
       6             : void
       7             : fd_prog_reclaim_enqueue( fd_progcache_join_t * join,
       8          96 :                          fd_progcache_rec_t *  rec ) {
       9          96 :   uint idx = (uint)( rec - join->rec.pool->ele );
      10          96 :   ulong rec_max = fd_prog_recp_ele_max( join->rec.pool );
      11          96 :   if( FD_UNLIKELY( (ulong)idx >= rec_max ) )
      12           0 :     FD_LOG_CRIT(( "progcache: corruption detected (reclaim_enqueue rec_idx=%u rec_max=%lu)", idx, rec_max ));
      13          96 :   rec->reclaim_next = join->rec.reclaim_head;
      14          96 :   join->rec.reclaim_head = idx;
      15          96 : }
      16             : 
      17             : static _Bool
      18             : rec_reclaim( fd_progcache_join_t * join,
      19         102 :              fd_progcache_rec_t *  rec ) {
      20             : 
      21             :   /* Remove the record from a transaction */
      22             : 
      23         102 :   ulong txn_max = fd_prog_txnp_max( join->txn.pool );
      24         102 :   uint txn_idx = atomic_load_explicit( &rec->txn_idx, memory_order_acquire );
      25         102 :   if( txn_idx!=UINT_MAX ) {
      26          36 :     if( FD_UNLIKELY( (ulong)txn_idx >= txn_max ) )
      27           0 :       FD_LOG_CRIT(( "progcache: corruption detected (rec_reclaim txn_idx=%u txn_max=%lu)", txn_idx, txn_max ));
      28          36 :     fd_progcache_txn_t * txn = &join->txn.pool[ txn_idx ];
      29          36 :     fd_rwlock_write( &txn->lock );
      30          36 :     fd_racesan_hook( "prog_reclaim:pre_cas" );
      31          36 :     if( atomic_compare_exchange_strong_explicit( &rec->txn_idx, &txn_idx, UINT_MAX, memory_order_acq_rel, memory_order_acquire ) ) {
      32             :       /* A transaction may not be deallocated before all records are
      33             :          unlinked. */
      34          36 :       fd_progcache_rec_unlink( join->rec.pool->ele, rec, txn, join->rec.pool->ele_max );
      35          36 :     } else {
      36             :       /* Strong CAS failure implies that another thread is already
      37             :          unlinking the record (the rooting logic) */
      38           0 :       FD_CHECK_CRIT( atomic_load_explicit( &rec->txn_idx, memory_order_relaxed )==UINT_MAX, "concurrency violation" );
      39           0 :     }
      40          36 :     fd_rwlock_unwrite( &txn->lock );
      41          36 :   }
      42         102 :   fd_racesan_hook( "prog_reclaim:post_unlink" );
      43             : 
      44             :   /* Drain existing users
      45             :      Leave record in locked state (lock is reset when allocating) */
      46             : 
      47         102 :   if( FD_UNLIKELY( !fd_rwlock_trywrite( &rec->lock ) ) ) return 0;
      48             : 
      49             :   /* All users are gone, deallocate record */
      50             : 
      51          96 :   rec->reclaim_next = UINT_MAX;
      52          96 :   fd_progcache_val_free( rec, join );
      53          96 :   rec->exists = 0;
      54          96 :   fd_prog_clock_remove( join->clock.bits, (ulong)( rec - join->rec.pool->ele ) );
      55          96 :   fd_prog_recp_release( join->rec.pool, rec );
      56          96 :   return 1;
      57         102 : }
      58             : 
      59             : ulong
      60        2646 : fd_prog_reclaim_work( fd_progcache_join_t * join ) {
      61        2646 :   ulong  rec_max = fd_prog_recp_ele_max( join->rec.pool );
      62        2646 :   ulong  cnt     = 0UL;
      63        2646 :   uint * prev_p  = &join->rec.reclaim_head;
      64        2646 :   uint   cur     = join->rec.reclaim_head;
      65        2748 :   while( cur!=UINT_MAX ) {
      66         102 :     if( FD_UNLIKELY( (ulong)cur >= rec_max ) )
      67           0 :       FD_LOG_CRIT(( "progcache: corruption detected (reclaim_work rec_idx=%u rec_max=%lu)", cur, rec_max ));
      68         102 :     fd_progcache_rec_t * rec = &join->rec.pool->ele[ cur ];
      69         102 :     uint next = rec->reclaim_next;
      70         102 :     if( rec_reclaim( join, rec ) ) {
      71          96 :       *prev_p = next;
      72          96 :       cnt++;
      73          96 :     } else {
      74           6 :       prev_p = &rec->reclaim_next;
      75           6 :     }
      76         102 :     cur = next;
      77         102 :   }
      78        2646 :   return cnt;
      79        2646 : }
      80             : 
      81             : long
      82             : fd_prog_delete_rec( fd_progcache_join_t * cache,
      83          99 :                     fd_progcache_rec_t *  rec ) {
      84          99 :   if( !rec ) return -1L;
      85             : 
      86             :   /* Prepare index removal, and bail if rec is no longer present in map */
      87          99 :   struct {
      88          99 :     fd_prog_recm_txn_t txn[1];
      89          99 :     fd_prog_recm_txn_private_info_t info[1];
      90          99 :   } _map_txn;
      91          99 :   fd_prog_recm_txn_t * map_txn = fd_prog_recm_txn_init( _map_txn.txn, cache->rec.map, 1UL );
      92          99 :   fd_prog_recm_txn_add( map_txn, &rec->pair, 1 );
      93          99 :   int txn_err = fd_prog_recm_txn_try( map_txn, FD_MAP_FLAG_BLOCKING );
      94          99 :   if( FD_UNLIKELY( txn_err!=FD_MAP_SUCCESS ) )
      95           0 :     FD_LOG_CRIT(( "fd_prog_recm_txn_try failed: %i-%s", txn_err, fd_map_strerror( txn_err ) ));
      96          99 :   fd_prog_recm_query_t query[1];
      97          99 :   int q_err = fd_prog_recm_txn_query( cache->rec.map, &rec->pair, NULL, query, 0 );
      98          99 :   if( q_err==FD_MAP_ERR_KEY || query->ele!=rec ) {
      99           3 :     fd_prog_recm_txn_test( map_txn );
     100           3 :     fd_prog_recm_txn_fini( map_txn );
     101           3 :     return -1L;
     102           3 :   }
     103             : 
     104             :   /* Drop record */
     105          96 :   int rm_err = fd_prog_recm_txn_remove( cache->rec.map, &rec->pair, NULL, query, 0 );
     106          96 :   if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) )
     107           0 :     FD_LOG_CRIT(( "fd_prog_recm_txn_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) ));
     108          96 :   int test_err = fd_prog_recm_txn_test( map_txn );
     109          96 :   if( FD_UNLIKELY( test_err!=FD_MAP_SUCCESS ) )
     110           0 :     FD_LOG_CRIT(( "fd_prog_recm_txn_test failed: %i-%s", test_err, fd_map_strerror( test_err ) ));
     111          96 :   fd_prog_recm_txn_fini( map_txn );
     112             : 
     113          96 :   fd_prog_reclaim_enqueue( cache, rec );
     114          96 :   return (long)rec->data_max;
     115          96 : }

Generated by: LCOV version 1.14