LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_txncache_shmem.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 111 129 86.0 %
Date: 2026-10-16 04:29:28 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "fd_txncache_shmem.h"
       2             : #include "fd_txncache_private.h"
       3             : 
       4             : #define POOL_NAME       blockcache_pool
       5         579 : #define POOL_T          fd_txncache_blockcache_shmem_t
       6             : #define POOL_IDX_T      ulong
       7      825216 : #define POOL_NEXT       pool.next
       8             : #define POOL_IMPL_STYLE 2
       9             : #include "../../util/tmpl/fd_pool.c"
      10             : 
      11             : #define MAP_NAME               blockhash_map
      12        8169 : #define MAP_KEY                blockhash
      13         918 : #define MAP_ELE_T              fd_txncache_blockcache_shmem_t
      14             : #define MAP_KEY_T              fd_hash_t
      15        7287 : #define MAP_PREV               blockhash_map.prev
      16        8169 : #define MAP_NEXT               blockhash_map.next
      17             : #define MAP_KEY_EQ(k0,k1)      fd_hash_eq( k0, k1 )
      18             : #define MAP_KEY_HASH(key,seed) (__extension__({ (void)(seed); fd_ulong_load_8_fast( (key)->uc ); }))
      19             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
      20             : #define MAP_MULTI              1
      21             : #define MAP_IMPL_STYLE         2
      22             : #include "../../util/tmpl/fd_map_chain.c"
      23             : 
      24             : #define SLIST_NAME       root_slist
      25             : #define SLIST_ELE_T      fd_txncache_blockcache_shmem_t
      26             : #define SLIST_IDX_T      ulong
      27           0 : #define SLIST_NEXT       slist.next
      28             : #define SLIST_IMPL_STYLE 2
      29             : #include "../../util/tmpl/fd_slist.c"
      30             : 
      31             : FD_FN_CONST ulong
      32             : fd_txncache_max_txnpages( ulong max_active_slots,
      33         744 :                           ulong max_txn_per_slot ) {
      34             :   /* The pool must hold the worst case set of simultaneously live
      35             :      transactions.  Entries stay live until their inserting fork's
      36             :      blockcache is removed, and rooted forks are retained for
      37             :      FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE further root advances, so up to
      38             :      max_active_slots forks' insertions are live at once.  The worst
      39             :      case is the boot transient: the snapshot root (holding the full
      40             :      staged snapshot load) plus every other pool fork full of live
      41             :      inserts.
      42             : 
      43             :      The snapshot transient retains at most 151 slot deltas at
      44             :      max_txn_per_slot entries each.  max_txn_per_slot is 2x the actual
      45             :      per slot transaction limit, because Agave snapshots contain each
      46             :      transaction twice (once keyed by signature, once by message hash,
      47             :      indistinguishably; see FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT).  Live
      48             :      inserts are 1x, so each non-snapshot fork holds at most
      49             :      max_txn_per_slot/2 entries.
      50             : 
      51             :      We count pages, not transactions, so pages might be wasted.  The
      52             :      maximum page wastage occurs when every blockcache has a partially
      53             :      full tail page, adding max_active_slots-1 pages beyond the global
      54             :      transaction count bound (one blockcache's tail is already counted
      55             :      by the ceiling division). */
      56             : 
      57         744 :   ulong snapshot_budget = FD_TXNCACHE_MAX_SLOT_DELTAS*max_txn_per_slot;
      58         744 :   ulong live_budget     = (max_active_slots-1UL)*((max_txn_per_slot+1UL)/2UL);
      59         744 :   return max_active_slots-1UL
      60         744 :        + 1UL+(snapshot_budget+live_budget-1UL)/FD_TXNCACHE_TXNS_PER_PAGE;
      61         744 : }
      62             : 
      63             : FD_FN_CONST ulong
      64             : fd_txncache_max_txnpages_per_blockhash( ulong max_active_slots,
      65         372 :                                         ulong max_txn_per_slot ) {
      66             :   /* The maximum number of transaction pages we might need to store all
      67             :      the transactions that could be seen in a blockhash.
      68             : 
      69             :      In the worst case, every transaction in every live bank refers to
      70             :      the same blockhash, but a blockhash can never hold more pages than
      71             :      exist in the pool. */
      72             : 
      73         372 :   ulong max_txnpages = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot );
      74         372 :   ulong result = 1UL+(max_txn_per_slot*max_active_slots)/FD_TXNCACHE_TXNS_PER_PAGE;
      75         372 :   return fd_ulong_min( result, max_txnpages );
      76         372 : }
      77             : 
      78             : FD_FN_CONST ulong
      79         639 : fd_txncache_shmem_align( void ) {
      80         639 :   return FD_TXNCACHE_SHMEM_ALIGN;
      81         639 : }
      82             : 
      83             : ulong
      84             : fd_txncache_shmem_footprint( ulong max_live_slots,
      85         216 :                              ulong max_txn_per_slot ) {
      86         216 :   if( FD_UNLIKELY( max_live_slots<1UL ) ) return 0UL;
      87         216 :   if( FD_UNLIKELY( max_txn_per_slot<1UL ) ) return 0UL;
      88             : 
      89         216 :   ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
      90         216 :   ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
      91         216 :   ulong bucket_cnt = fd_txncache_bucket_cnt( max_txn_per_slot );
      92             : 
      93         216 :   ulong _max_txnpages               = fd_txncache_max_txnpages              ( max_active_slots, max_txn_per_slot );
      94         216 :   ulong _max_txnpages_per_blockhash = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot );
      95         216 :   if( FD_UNLIKELY( _max_txnpages>FD_TXNCACHE_MAX_TXNPAGES ) )
      96           0 :     FD_LOG_ERR(( "txncache needs %lu txnpages but at most %lu are addressable (max_live_slots=%lu max_txn_per_slot=%lu)",
      97         216 :                  _max_txnpages, FD_TXNCACHE_MAX_TXNPAGES, max_live_slots, max_txn_per_slot ));
      98         216 :   ulong _txnpage_idx_sz = fd_txncache_txnpage_idx_sz( _max_txnpages );
      99             : 
     100         216 :   ulong _descends_footprint = descends_set_footprint( max_active_slots );
     101         216 :   if( FD_UNLIKELY( !_descends_footprint ) ) return 0UL;
     102             : 
     103         216 :   ulong l;
     104         216 :   l = FD_LAYOUT_INIT;
     105         216 :   l = FD_LAYOUT_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN,        sizeof(fd_txncache_shmem_t)                                   );
     106         216 :   l = FD_LAYOUT_APPEND( l, blockhash_map_align(),          blockhash_map_footprint( blockhash_map_chains )               );
     107         216 :   l = FD_LAYOUT_APPEND( l, blockcache_pool_align(),        blockcache_pool_footprint( max_active_slots )                 );
     108         216 :   l = FD_LAYOUT_APPEND( l, _txnpage_idx_sz,                max_active_slots*_max_txnpages_per_blockhash*_txnpage_idx_sz ); /* blockcache->pages */
     109         216 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                  max_active_slots*bucket_cnt*sizeof(uint)                      ); /* blockcache->heads */
     110         216 :   l = FD_LAYOUT_APPEND( l, descends_set_align(),           max_active_slots*_descends_footprint                          ); /* blockcache->descends */
     111         216 :   l = FD_LAYOUT_APPEND( l, _txnpage_idx_sz,                _max_txnpages*_txnpage_idx_sz                                 ); /* txnpages_free */
     112         216 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t), _max_txnpages*sizeof(fd_txncache_txnpage_t)                   ); /* txnpages */
     113         216 :   l = FD_LAYOUT_APPEND( l, _txnpage_idx_sz,                _max_txnpages_per_blockhash*_txnpage_idx_sz                   ); /* scratchpad txnpage pointer array for purge stale */
     114         216 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                  bucket_cnt*sizeof(uint)                                       ); /* scratchpad heads for purge stale */
     115         216 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t), sizeof(fd_txncache_txnpage_t)                                 ); /* scratchpad txnpage for purge stale */
     116         216 :   return FD_LAYOUT_FINI( l, FD_TXNCACHE_SHMEM_ALIGN );
     117         216 : }
     118             : 
     119             : void *
     120             : fd_txncache_shmem_new( void * shmem,
     121             :                        ulong  max_live_slots,
     122             :                        ulong  max_txn_per_slot,
     123         156 :                        ulong  seed ) {
     124         156 :   if( FD_UNLIKELY( !shmem ) ) {
     125           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     126           0 :     return NULL;
     127           0 :   }
     128             : 
     129         156 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_txncache_shmem_align() ) ) ) {
     130           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     131           0 :     return NULL;
     132           0 :   }
     133             : 
     134         156 :   if( FD_UNLIKELY( !max_live_slots ) ) return NULL;
     135         156 :   if( FD_UNLIKELY( !max_txn_per_slot ) ) return NULL;
     136             : 
     137         156 :   ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
     138         156 :   ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
     139         156 :   ulong bucket_cnt = fd_txncache_bucket_cnt( max_txn_per_slot );
     140             : 
     141         156 :   ulong _max_txnpages               = fd_txncache_max_txnpages              ( max_active_slots, max_txn_per_slot );
     142         156 :   ulong _max_txnpages_per_blockhash = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot );
     143         156 :   if( FD_UNLIKELY( _max_txnpages>FD_TXNCACHE_MAX_TXNPAGES ) )
     144           0 :     FD_LOG_ERR(( "txncache needs %lu txnpages but at most %lu are addressable (max_live_slots=%lu max_txn_per_slot=%lu)",
     145         156 :                  _max_txnpages, FD_TXNCACHE_MAX_TXNPAGES, max_live_slots, max_txn_per_slot ));
     146         156 :   ulong _txnpage_idx_sz = fd_txncache_txnpage_idx_sz( _max_txnpages );
     147             : 
     148         156 :   ulong _descends_footprint = descends_set_footprint( max_active_slots );
     149         156 :   if( FD_UNLIKELY( !_descends_footprint ) ) return NULL;
     150             : 
     151         156 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     152         156 :   fd_txncache_shmem_t * tc    = FD_SCRATCH_ALLOC_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN,         sizeof(fd_txncache_shmem_t)                                   );
     153         156 :   void * _blockhash_map       = FD_SCRATCH_ALLOC_APPEND( l, blockhash_map_align(),           blockhash_map_footprint( blockhash_map_chains )               );
     154         156 :   void * _blockcache_pool     = FD_SCRATCH_ALLOC_APPEND( l, blockcache_pool_align(),         blockcache_pool_footprint( max_active_slots )                 );
     155         156 :                                 FD_SCRATCH_ALLOC_APPEND( l, _txnpage_idx_sz,                 max_active_slots*_max_txnpages_per_blockhash*_txnpage_idx_sz );
     156         156 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                   max_active_slots*bucket_cnt*sizeof(uint)                      );
     157         156 :   void * _blockcache_descends = FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(),            max_active_slots*_descends_footprint                          );
     158         156 :   void * _txnpages_free       = FD_SCRATCH_ALLOC_APPEND( l, _txnpage_idx_sz,                 _max_txnpages*_txnpage_idx_sz                                 );
     159         156 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),  _max_txnpages*sizeof(fd_txncache_txnpage_t)                   );
     160         156 :                                 FD_SCRATCH_ALLOC_APPEND( l, _txnpage_idx_sz,                 _max_txnpages_per_blockhash*_txnpage_idx_sz                   );
     161         156 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                   bucket_cnt*sizeof(uint)                                       );
     162         156 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),  sizeof(fd_txncache_txnpage_t)                                 );
     163             : 
     164         156 :   fd_txncache_blockcache_shmem_t * blockcache_pool = blockcache_pool_join( blockcache_pool_new( _blockcache_pool, max_active_slots ) );
     165         156 :   FD_TEST( blockcache_pool );
     166       25632 :   for( ulong i=0UL; i<max_active_slots; i++ ) blockcache_pool[ i ].frozen = -1;
     167             : 
     168         156 :   blockhash_map_t * blockhash_map = blockhash_map_join( blockhash_map_new( _blockhash_map, blockhash_map_chains, 0UL /* seed not used */ ) );
     169         156 :   FD_TEST( blockhash_map );
     170             : 
     171       25632 :   for( ulong i=0UL; i<max_active_slots; i++ ) {
     172       25476 :     descends_set_t * descends_set = descends_set_join( descends_set_new( (uchar *)_blockcache_descends + i*_descends_footprint, max_active_slots ) );
     173       25476 :     FD_TEST( descends_set );
     174       25476 :   }
     175             : 
     176         156 :   tc->root_cnt     = 0UL;
     177         156 :   tc->mutation_gen = 0UL;
     178         156 :   tc->root_gen     = 0UL;
     179         156 :   FD_TEST( root_slist_join( root_slist_new( tc->root_ll ) ) );
     180             : 
     181         156 :   tc->lock->value = 0;
     182             : 
     183         156 :   tc->txn_per_slot_max           = max_txn_per_slot;
     184         156 :   tc->active_slots_max           = max_active_slots;
     185         156 :   tc->bucket_cnt                 = bucket_cnt;
     186         156 :   tc->txnpages_per_blockhash_max = _max_txnpages_per_blockhash;
     187         156 :   tc->max_txnpages               = _max_txnpages;
     188         156 :   tc->txnpage_idx_sz             = _txnpage_idx_sz;
     189             : 
     190         156 :   tc->blockcache_generation = 0U;
     191         156 :   tc->txnpages_free_cnt = _max_txnpages;
     192      193983 :   for( ulong i=0UL; i<_max_txnpages; i++ ) fd_txncache_txnpage_idx_st( _txnpage_idx_sz, _txnpages_free, i, i );
     193             : 
     194         156 :   tc->seed = seed;
     195             : 
     196         156 :   FD_COMPILER_MFENCE();
     197         156 :   FD_VOLATILE( tc->magic ) = FD_TXNCACHE_SHMEM_MAGIC;
     198         156 :   FD_COMPILER_MFENCE();
     199             : 
     200         156 :   return (void *)tc;
     201         156 : }
     202             : 
     203             : fd_txncache_shmem_t *
     204         267 : fd_txncache_shmem_join( void * shtc ) {
     205         267 :   if( FD_UNLIKELY( !shtc ) ) {
     206           0 :     FD_LOG_WARNING(( "NULL shtc" ));
     207           0 :     return NULL;
     208           0 :   }
     209             : 
     210         267 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtc, fd_txncache_shmem_align() ) ) ) {
     211           0 :     FD_LOG_WARNING(( "misaligned shtc" ));
     212           0 :     return NULL;
     213           0 :   }
     214             : 
     215         267 :   fd_txncache_shmem_t * tc = (fd_txncache_shmem_t *)shtc;
     216             : 
     217         267 :   if( FD_UNLIKELY( tc->magic!=FD_TXNCACHE_SHMEM_MAGIC ) ) {
     218           0 :     FD_LOG_WARNING(( "bad magic" ));
     219           0 :     return NULL;
     220           0 :   }
     221             : 
     222         267 :   return tc;
     223         267 : }

Generated by: LCOV version 1.14