LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_txncache_shmem.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 115 133 86.5 %
Date: 2026-08-28 06:53:58 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         189 : #define POOL_T          fd_txncache_blockcache_shmem_t
       6             : #define POOL_IDX_T      ulong
       7      662421 : #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        4299 : #define MAP_KEY                blockhash
      13           0 : #define MAP_ELE_T              fd_txncache_blockcache_shmem_t
      14             : #define MAP_KEY_T              fd_hash_t
      15        4299 : #define MAP_PREV               blockhash_map.prev
      16        4299 : #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 ushort
      32             : fd_txncache_max_txnpages( ulong max_active_slots,
      33             :                           ulong max_txn_per_slot,
      34         366 :                           int   larger_max_cost_per_block ) {
      35             :   /* The pool must hold the worst case set of simultaneously live
      36             :      transactions.  Entries stay live until their inserting fork's
      37             :      blockcache is removed, and rooted forks are retained for
      38             :      FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE further root advances, so up to
      39             :      max_active_slots forks' insertions are live at once.  The worst
      40             :      case is the boot transient: the snapshot root (holding the full
      41             :      staged snapshot load) plus every other pool fork full of live
      42             :      inserts.
      43             : 
      44             :      The snapshot transient retains at most 151 slot deltas at
      45             :      max_txn_per_slot entries each.  max_txn_per_slot is 2x the actual
      46             :      per slot transaction limit, because Agave snapshots contain each
      47             :      transaction twice (once keyed by signature, once by message hash,
      48             :      indistinguishably; see FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT).  Live
      49             :      inserts are 1x, so each non-snapshot fork holds at most
      50             :      max_txn_per_slot/2 entries.
      51             : 
      52             :      We count pages, not transactions, so pages might be wasted.  The
      53             :      maximum page wastage occurs when every blockcache has a partially
      54             :      full tail page, adding max_active_slots-1 pages beyond the global
      55             :      transaction count bound (one blockcache's tail is already counted
      56             :      by the ceiling division). */
      57             : 
      58         366 :   ulong result;
      59         366 :   if( FD_UNLIKELY( larger_max_cost_per_block ) ) {
      60             :     /* Raised block cost limits invalidate the 1x live bound.  Fall
      61             :        back to every active slot simultaneously full. */
      62           0 :     result = max_active_slots-1UL+max_active_slots*(1UL+(max_txn_per_slot-1UL)/FD_TXNCACHE_TXNS_PER_PAGE);
      63         366 :   } else {
      64         366 :     ulong snapshot_budget = FD_TXNCACHE_MAX_SLOT_DELTAS*max_txn_per_slot;
      65         366 :     ulong live_budget     = (max_active_slots-1UL)*((max_txn_per_slot+1UL)/2UL);
      66         366 :     result = max_active_slots-1UL
      67         366 :            + 1UL+(snapshot_budget+live_budget-1UL)/FD_TXNCACHE_TXNS_PER_PAGE;
      68         366 :   }
      69         366 :   if( FD_UNLIKELY( result>USHORT_MAX-2UL ) ) return 0; /* MAX is the invalid flag, MAX-1 is the xbusy flag. */
      70         366 :   return (ushort)result;
      71         366 : }
      72             : 
      73             : FD_FN_CONST ushort
      74             : fd_txncache_max_txnpages_per_blockhash( ulong max_active_slots,
      75             :                                         ulong max_txn_per_slot,
      76         183 :                                         int   larger_max_cost_per_block ) {
      77             :   /* The maximum number of transaction pages we might need to store all
      78             :      the transactions that could be seen in a blockhash.
      79             : 
      80             :      In the worst case, every transaction in every live bank refers to
      81             :      the same blockhash, but a blockhash can never hold more pages than
      82             :      exist in the pool. */
      83             : 
      84         183 :   ulong max_txnpages = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot, larger_max_cost_per_block );
      85         183 :   if( FD_UNLIKELY( !max_txnpages ) ) return 0;
      86             : 
      87         183 :   ulong result = 1UL+(max_txn_per_slot*max_active_slots)/FD_TXNCACHE_TXNS_PER_PAGE;
      88         183 :   result = fd_ulong_min( result, max_txnpages );
      89         183 :   if( FD_UNLIKELY( result>USHORT_MAX-2UL ) ) return 0; /* MAX is the invalid flag, MAX-1 is the xbusy flag. */
      90         183 :   return (ushort)result;
      91         183 : }
      92             : 
      93             : FD_FN_CONST ulong
      94         249 : fd_txncache_shmem_align( void ) {
      95         249 :   return FD_TXNCACHE_SHMEM_ALIGN;
      96         249 : }
      97             : 
      98             : FD_FN_CONST ulong
      99             : fd_txncache_shmem_footprint( ulong max_live_slots,
     100             :                              ulong max_txn_per_slot,
     101         120 :                              int   larger_max_cost_per_block ) {
     102         120 :   if( FD_UNLIKELY( max_live_slots<1UL ) ) return 0UL;
     103         120 :   if( FD_UNLIKELY( max_txn_per_slot<1UL ) ) return 0UL;
     104             : 
     105         120 :   ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
     106         120 :   ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
     107         120 :   ulong bucket_cnt = fd_txncache_bucket_cnt( max_txn_per_slot );
     108             : 
     109             :   /* To save memory, txnpages are referenced as ushort which is enough
     110             :      to support mainnet parameters without overflow. */
     111         120 :   ushort _max_txnpages = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot, larger_max_cost_per_block );
     112         120 :   if( FD_UNLIKELY( !_max_txnpages ) ) return 0UL;
     113             : 
     114         120 :   ulong _max_txnpages_per_blockhash = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot, larger_max_cost_per_block );
     115         120 :   if( FD_UNLIKELY( !_max_txnpages_per_blockhash ) ) return 0UL;
     116             : 
     117         120 :   ulong _descends_footprint = descends_set_footprint( max_active_slots );
     118         120 :   if( FD_UNLIKELY( !_descends_footprint ) ) return 0UL;
     119             : 
     120         120 :   ulong l;
     121         120 :   l = FD_LAYOUT_INIT;
     122         120 :   l = FD_LAYOUT_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN,        sizeof(fd_txncache_shmem_t)                                 );
     123         120 :   l = FD_LAYOUT_APPEND( l, blockhash_map_align(),          blockhash_map_footprint( blockhash_map_chains )             );
     124         120 :   l = FD_LAYOUT_APPEND( l, blockcache_pool_align(),        blockcache_pool_footprint( max_active_slots )               );
     125         120 :   l = FD_LAYOUT_APPEND( l, alignof(ushort),                max_active_slots*_max_txnpages_per_blockhash*sizeof(ushort) ); /* blockcache->pages */
     126         120 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                  max_active_slots*bucket_cnt*sizeof(uint)                    ); /* blockcache->heads */
     127         120 :   l = FD_LAYOUT_APPEND( l, descends_set_align(),           max_active_slots*_descends_footprint                        ); /* blockcache->descends */
     128         120 :   l = FD_LAYOUT_APPEND( l, alignof(ushort),                _max_txnpages*sizeof(ushort)                                ); /* txnpages_free */
     129         120 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t), _max_txnpages*sizeof(fd_txncache_txnpage_t)                 ); /* txnpages */
     130         120 :   l = FD_LAYOUT_APPEND( l, alignof(ushort),                _max_txnpages_per_blockhash*sizeof(ushort)                  ); /* scratchpad txnpage pointer array for purge stale */
     131         120 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                  bucket_cnt*sizeof(uint)                                     ); /* scratchpad heads for purge stale */
     132         120 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t), sizeof(fd_txncache_txnpage_t)                               ); /* scratchpad txnpage for purge stale */
     133         120 :   return FD_LAYOUT_FINI( l, FD_TXNCACHE_SHMEM_ALIGN );
     134         120 : }
     135             : 
     136             : void *
     137             : fd_txncache_shmem_new( void * shmem,
     138             :                        ulong  max_live_slots,
     139             :                        ulong  max_txn_per_slot,
     140             :                        int    larger_max_cost_per_block,
     141          63 :                        ulong  seed ) {
     142          63 :   if( FD_UNLIKELY( !shmem ) ) {
     143           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     144           0 :     return NULL;
     145           0 :   }
     146             : 
     147          63 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_txncache_shmem_align() ) ) ) {
     148           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     149           0 :     return NULL;
     150           0 :   }
     151             : 
     152          63 :   if( FD_UNLIKELY( !max_live_slots ) ) return NULL;
     153          63 :   if( FD_UNLIKELY( !max_txn_per_slot ) ) return NULL;
     154             : 
     155          63 :   ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
     156          63 :   ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
     157          63 :   ulong bucket_cnt = fd_txncache_bucket_cnt( max_txn_per_slot );
     158             : 
     159          63 :   ushort _max_txnpages               = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot, larger_max_cost_per_block );
     160          63 :   ushort _max_txnpages_per_blockhash = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot, larger_max_cost_per_block );
     161             : 
     162          63 :   if( FD_UNLIKELY( !_max_txnpages ) ) return NULL;
     163          63 :   if( FD_UNLIKELY( !_max_txnpages_per_blockhash ) ) return NULL;
     164             : 
     165          63 :   ulong _descends_footprint = descends_set_footprint( max_active_slots );
     166          63 :   if( FD_UNLIKELY( !_descends_footprint ) ) return NULL;
     167             : 
     168          63 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     169          63 :   fd_txncache_shmem_t * tc    = FD_SCRATCH_ALLOC_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN,         sizeof(fd_txncache_shmem_t)                                 );
     170          63 :   void * _blockhash_map       = FD_SCRATCH_ALLOC_APPEND( l, blockhash_map_align(),           blockhash_map_footprint( blockhash_map_chains )             );
     171          63 :   void * _blockcache_pool     = FD_SCRATCH_ALLOC_APPEND( l, blockcache_pool_align(),         blockcache_pool_footprint( max_active_slots )               );
     172          63 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(ushort),                 max_active_slots*_max_txnpages_per_blockhash*sizeof(ushort) );
     173          63 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                   max_active_slots*bucket_cnt*sizeof(uint)                    );
     174          63 :   void * _blockcache_descends = FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(),            max_active_slots*_descends_footprint                        );
     175          63 :   void * _txnpages_free       = FD_SCRATCH_ALLOC_APPEND( l, alignof(ushort),                 _max_txnpages*sizeof(ushort)                                );
     176          63 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),  _max_txnpages*sizeof(fd_txncache_txnpage_t)                 );
     177          63 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(ushort),                 _max_txnpages_per_blockhash*sizeof(ushort)                  );
     178          63 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                   bucket_cnt*sizeof(uint)                                     );
     179          63 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),  sizeof(fd_txncache_txnpage_t)                               );
     180             : 
     181          63 :   fd_txncache_blockcache_shmem_t * blockcache_pool = blockcache_pool_join( blockcache_pool_new( _blockcache_pool, max_active_slots ) );
     182          63 :   FD_TEST( blockcache_pool );
     183       10683 :   for( ulong i=0UL; i<max_active_slots; i++ ) blockcache_pool[ i ].frozen = -1;
     184             : 
     185          63 :   blockhash_map_t * blockhash_map = blockhash_map_join( blockhash_map_new( _blockhash_map, blockhash_map_chains, 0UL /* seed not used */ ) );
     186          63 :   FD_TEST( blockhash_map );
     187             : 
     188       10683 :   for( ulong i=0UL; i<max_active_slots; i++ ) {
     189       10620 :     descends_set_t * descends_set = descends_set_join( descends_set_new( (uchar *)_blockcache_descends + i*_descends_footprint, max_active_slots ) );
     190       10620 :     FD_TEST( descends_set );
     191       10620 :   }
     192             : 
     193          63 :   tc->root_cnt = 0UL;
     194          63 :   FD_TEST( root_slist_join( root_slist_new( tc->root_ll ) ) );
     195             : 
     196          63 :   tc->lock->value = 0;
     197             : 
     198          63 :   tc->txn_per_slot_max           = max_txn_per_slot;
     199          63 :   tc->active_slots_max           = max_active_slots;
     200          63 :   tc->bucket_cnt                 = bucket_cnt;
     201          63 :   tc->txnpages_per_blockhash_max = _max_txnpages_per_blockhash;
     202          63 :   tc->max_txnpages               = _max_txnpages;
     203             : 
     204          63 :   tc->blockcache_generation = 0U;
     205          63 :   tc->txnpages_free_cnt = _max_txnpages;
     206          63 :   ushort * txnpages_free = (ushort *)_txnpages_free;
     207       11175 :   for( ushort i=0; i<_max_txnpages; i++ ) txnpages_free[ i ] = i;
     208             : 
     209          63 :   tc->seed = seed;
     210             : 
     211          63 :   FD_COMPILER_MFENCE();
     212          63 :   FD_VOLATILE( tc->magic ) = FD_TXNCACHE_SHMEM_MAGIC;
     213          63 :   FD_COMPILER_MFENCE();
     214             : 
     215          63 :   return (void *)tc;
     216          63 : }
     217             : 
     218             : fd_txncache_shmem_t *
     219          63 : fd_txncache_shmem_join( void * shtc ) {
     220          63 :   if( FD_UNLIKELY( !shtc ) ) {
     221           0 :     FD_LOG_WARNING(( "NULL shtc" ));
     222           0 :     return NULL;
     223           0 :   }
     224             : 
     225          63 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtc, fd_txncache_shmem_align() ) ) ) {
     226           0 :     FD_LOG_WARNING(( "misaligned shtc" ));
     227           0 :     return NULL;
     228           0 :   }
     229             : 
     230          63 :   fd_txncache_shmem_t * tc = (fd_txncache_shmem_t *)shtc;
     231             : 
     232          63 :   if( FD_UNLIKELY( tc->magic!=FD_TXNCACHE_SHMEM_MAGIC ) ) {
     233           0 :     FD_LOG_WARNING(( "bad magic" ));
     234           0 :     return NULL;
     235           0 :   }
     236             : 
     237          63 :   return tc;
     238          63 : }

Generated by: LCOV version 1.14