LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 78 86 90.7 %
Date: 2026-07-18 05:25:05 Functions: 17 40 42.5 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_accdb_fd_accdb_private_h
       2             : #define HEADER_fd_src_flamenco_accdb_fd_accdb_private_h
       3             : 
       4             : #include "fd_accdb_shmem.h"
       5             : #include "fd_accdb_cache.h"
       6             : 
       7             : /* Maximum accounts a single acquire may request.
       8             :    FD_ACCDB_MAX_TX_ACCOUNT_LOCKS mirrors the mainnet per-transaction
       9             :    account-lock limit (Agave get_transaction_account_lock_limit == 64;
      10             :    the increase_tx_account_lock_limit feature -> 128 is assumed never to
      11             :    activate).  FD_ACCDB_MAX_TXN_PER_ACQUIRE mirrors
      12             :    FD_PACK_MAX_TXN_PER_BUNDLE, a bundle coalesces up to that many
      13             :    transactions into one acquire. */
      14      272376 : #define FD_ACCDB_MAX_TX_ACCOUNT_LOCKS (64UL)
      15           0 : #define FD_ACCDB_MAX_TXN_PER_ACQUIRE  (5UL)
      16           0 : #define FD_ACCDB_MAX_ACQUIRE_CNT      (FD_ACCDB_MAX_TXN_PER_ACQUIRE*FD_ACCDB_MAX_TX_ACCOUNT_LOCKS)
      17             : 
      18             : static inline void
      19          42 : spin_lock_acquire( int * lock ) {
      20          42 : # if FD_HAS_THREADS
      21          42 :   for(;;) {
      22          42 :     if( FD_LIKELY( !FD_ATOMIC_CAS( lock, 0, 1 ) ) ) break;
      23           0 :     FD_SPIN_PAUSE();
      24           0 :   }
      25             : # else
      26             :   *lock = 1;
      27             : # endif
      28          42 :   FD_COMPILER_MFENCE();
      29          42 : }
      30             : 
      31             : static inline void
      32          42 : spin_lock_release( int * lock ) {
      33          42 :   FD_COMPILER_MFENCE();
      34          42 : # if FD_HAS_THREADS
      35          42 :   FD_VOLATILE( *lock ) = 0;
      36             : # else
      37             :   *lock = 0;
      38             : # endif
      39          42 : }
      40             : 
      41             : #ifndef FD_ACCDB_NO_FORK_ID
      42             : struct fd_accdb_fork_id { ushort val; };
      43             : typedef struct fd_accdb_fork_id fd_accdb_fork_id_t;
      44             : #endif
      45             : 
      46             : struct __attribute__((packed)) fd_accdb_disk_meta {
      47             :   uchar pubkey[ 32UL ];
      48             :   uint  size;
      49             :   uchar owner[ 32UL ];
      50             : };
      51             : 
      52             : typedef struct fd_accdb_disk_meta fd_accdb_disk_meta_t;
      53             : 
      54             : struct fd_accdb_txn {
      55             :   union {
      56             :     struct { uint next; } pool;
      57             :     struct { uint next; } fork;
      58             :   };
      59             : 
      60             :   uint acc_map_idx;
      61             :   uint acc_pool_idx;
      62             : };
      63             : 
      64             : typedef struct fd_accdb_txn fd_accdb_txn_t;
      65             : 
      66             : #define POOL_NAME       txn_pool
      67      421245 : #define POOL_ELE_T      fd_accdb_txn_t
      68        2376 : #define POOL_NEXT       pool.next
      69             : #define POOL_IDX_T      uint
      70      970035 : #define POOL_IDX_WIDTH  32
      71             : #define POOL_IMPL_STYLE 0
      72             : #define POOL_LAZY       1
      73             : 
      74             : #include "../../util/tmpl/fd_pool_para.c"
      75             : 
      76             : #define SET_NAME       descends_set
      77             : #define SET_IMPL_STYLE 1
      78             : #include "../../util/tmpl/fd_set_dynamic.c"
      79             : 
      80             : struct fd_accdb_fork_shmem {
      81             :   uint generation;
      82             : 
      83             :   fd_accdb_fork_id_t parent_id;
      84             :   fd_accdb_fork_id_t child_id;
      85             :   fd_accdb_fork_id_t sibling_id;
      86             : 
      87             :   struct {
      88             :     ulong next;
      89             :   } pool;
      90             : 
      91             :   uint txn_head;
      92             : };
      93             : 
      94             : typedef struct fd_accdb_fork_shmem fd_accdb_fork_shmem_t;
      95             : 
      96             : #define POOL_NAME       fork_pool
      97       92958 : #define POOL_ELE_T      fd_accdb_fork_shmem_t
      98       73320 : #define POOL_NEXT       pool.next
      99             : #define POOL_IDX_T      ulong
     100             : #define POOL_IMPL_STYLE 0
     101             : 
     102             : #include "../../util/tmpl/fd_pool_para.c"
     103             : 
     104             : struct fd_accdb_partition {
     105             :   ulong marked_compaction;
     106             :   ulong write_offset;
     107             :   ulong compaction_offset;
     108             : 
     109             :   ulong bytes_freed;
     110             : 
     111             :   uchar layer; /* compaction tier this partition belongs to. */
     112             : 
     113             :   ulong read_ops;
     114             :   ulong bytes_read;
     115             :   ulong write_ops;
     116             :   ulong bytes_written;
     117             : 
     118             :   /* Tickcount (fd_tickcount) of the partition's lifecycle events.  Set
     119             :      only at partition creation and again when the partition closes
     120             :      (i.e. the layer's write head rotates off it). */
     121             :   long created_ticks;
     122             :   long filled_ticks;
     123             : 
     124             :   /* Compaction lifecycle flags.  queued is set when the partition is
     125             :      pushed onto the compaction_dlist and cleared when it is popped.
     126             :      compacting_now is set by the compaction tile around the actual
     127             :      compaction work for this partition. */
     128             :   uchar queued;
     129             :   uchar compacting_now;
     130             : 
     131             :   /* Per-compaction-pass telemetry accumulators for the
     132             :      accdb_compaction_completed event.  Reset when a partition's
     133             :      compaction begins (compacting_now set in background_compact) and
     134             :      accumulated as records are scanned/relocated. */
     135             :   long  compaction_start_wallclock;    /* timestamp when this pass began */
     136             :   ulong compaction_accounts_relocated; /* live records moved this pass */
     137             :   ulong compaction_bytes_relocated;    /* bytes moved this pass */
     138             :   ulong compaction_dead_records;       /* records skipped (no live index entry) this pass */
     139             : 
     140             :   /* Epoch at which this partition was enqueued for compaction.  Set by
     141             :      fd_accdb_shmem_bytes_freed when the partition crosses the
     142             :      freed-bytes threshold.  The compaction tile will not begin reading
     143             :      from this partition until all joiners that were in an
     144             :      epoch-protected critical section at enqueue time have exited,
     145             :      ensuring any in-flight pwritev2 to this partition has completed. */
     146             :   ulong compaction_ready_epoch;
     147             : 
     148             :   /* Epoch at which this partition was enqueued for deferred freeing.
     149             :      Set by compaction when the partition finishes compaction, and
     150             :      checked by the reclamation scan to determine when it is safe to
     151             :      release the partition back to the pool. */
     152             :   ulong epoch_tag;
     153             : 
     154             :   ulong pool_next;
     155             : 
     156             :   ulong dlist_prev;
     157             :   ulong dlist_next;
     158             : };
     159             : 
     160             : typedef struct fd_accdb_partition fd_accdb_partition_t;
     161             : 
     162             : #define POOL_NAME       partition_pool
     163             : #define POOL_T          fd_accdb_partition_t
     164          42 : #define POOL_NEXT       pool_next
     165             : #define POOL_IDX_T      ulong
     166             : #define POOL_IMPL_STYLE 1
     167             : 
     168             : #include "../../util/tmpl/fd_pool.c"
     169             : 
     170             : #define DLIST_NAME       compaction_dlist
     171             : #define DLIST_ELE_T      fd_accdb_partition_t
     172          24 : #define DLIST_PREV       dlist_prev
     173          24 : #define DLIST_NEXT       dlist_next
     174             : #define DLIST_IMPL_STYLE 1
     175             : 
     176             : #include "../../util/tmpl/fd_dlist.c"
     177             : 
     178             : /* deferred_free_dlist reuses the same prev/next fields as
     179             :    compaction_dlist.  A partition is in at most one of the two lists at
     180             :    any time: it is popped from compaction_dlist before being pushed onto
     181             :    deferred_free_dlist. */
     182             : 
     183             : #define DLIST_NAME       deferred_free_dlist
     184             : #define DLIST_ELE_T      fd_accdb_partition_t
     185           0 : #define DLIST_PREV       dlist_prev
     186           0 : #define DLIST_NEXT       dlist_next
     187             : #define DLIST_IMPL_STYLE 1
     188             : 
     189             : #include "../../util/tmpl/fd_dlist.c"
     190             : 
     191             : struct fd_accdb_cache_key {
     192             :   uchar pubkey[ 32UL ];
     193             :   uint generation;
     194             : };
     195             : 
     196             : typedef struct fd_accdb_cache_key fd_accdb_cache_key_t;
     197             : 
     198             : struct __attribute__((aligned(64))) fd_accdb_accmeta {
     199             :   fd_accdb_cache_key_t key;
     200             : 
     201             :   struct {
     202             :     uint next;
     203             :   } map;
     204             : 
     205             :   union {
     206             :     struct {
     207             :       uint next;
     208             :     } pool;
     209             :     uint cache_idx;
     210             :   };
     211             : 
     212             :   uint   executable_size;
     213             : 
     214             :   ulong  lamports;
     215             : 
     216             :   /* Pack offset and fork_id together into a single ulong to pack the
     217             :      struct into a single 64 byte cache line.  This is a performance win
     218             :      of 2-3%. */
     219             :   ulong  offset_fork;
     220             : };
     221             : 
     222             : typedef struct fd_accdb_accmeta fd_accdb_accmeta_t;
     223             : 
     224             : FD_STATIC_ASSERT( alignof(fd_accdb_accmeta_t)==64, layout );
     225             : FD_STATIC_ASSERT( sizeof (fd_accdb_accmeta_t)==64, layout );
     226             : 
     227      452193 : #define FD_ACCDB_OFF_BITS  48UL
     228      332550 : #define FD_ACCDB_OFF_MASK  ((1UL<<FD_ACCDB_OFF_BITS)-1UL)       /* 0x0000_FFFF_FFFF_FFFF */
     229      213921 : #define FD_ACCDB_OFF_INVAL FD_ACCDB_OFF_MASK                    /* sentinel: offset bits all-ones */
     230             : 
     231             : /* The `size` field in fd_accdb_disk_meta_t (named executable_size in
     232             :    fd_accdb_accmeta_t) packs five things into 32 bits:
     233             : 
     234             :      bit  31     executable flag                       (FD_ACCDB_SIZE_EXEC_BIT)
     235             :      bit  30     cache_valid flag, in-memory only      (FD_ACCDB_SIZE_CACHE_VALID_BIT)
     236             :      bit  29     cache_claim flag, in-memory only      (FD_ACCDB_SIZE_CACHE_CLAIM_BIT)
     237             :      bit  28     pd_write flag,    in-memory only      (FD_ACCDB_SIZE_PD_WRITE_BIT)
     238             :      bits 27..0  data length in bytes                  (FD_ACCDB_SIZE_MASK)
     239             : 
     240             :    The data length is therefore 28 bits, max 256 MiB, still well above
     241             :    FD_RUNTIME_ACC_SZ_MAX of 10 MiB (enforced by the static assert below).
     242             : 
     243             :    The three upper flag bits exist only in the in-memory index, never on
     244             :    disk:
     245             :      - cache_valid (bit 30): when set, cache_idx holds a valid
     246             :        (class, idx) pair; when clear, cache_idx must not be dereferenced
     247             :        (it may hold a snapshot slot number or garbage).
     248             :      - cache_claim (bit 29): a short-lived eviction/install lock taken
     249             :        via CAS while a writer mutates the cache_idx <-> cache-line
     250             :        binding, so concurrent readers and the evictor do not race.
     251             :      - pd_write (bit 28): set on a committed version whose write changed
     252             :        BPF upgradeable-loader deploy status this slot (Deploy/Upgrade/
     253             :        Extend/Close).  Meaningful only while the accmeta's key.generation
     254             :        equals a live fork's generation (i.e. the version was committed on
     255             :        that fork this slot); across snapshot/root boundaries the bit is
     256             :        dead by construction because the generation no longer matches any
     257             :        live fork.  Carried explicitly by the two commit sites in
     258             :        fd_accdb_release and nowhere else.
     259             : 
     260             :    The on-disk representation (written via SIZE_PACK / SIZE_DATA) carries
     261             :    no in-memory flag: persisted bytes are unchanged, and compaction's
     262             :    copy_file_range preserves the record headers verbatim without
     263             :    rewriting them. */
     264             : 
     265      136137 : #define FD_ACCDB_SIZE_EXEC_BIT        (1U<<31)
     266        1869 : #define FD_ACCDB_SIZE_CACHE_VALID_BIT (1U<<30)
     267        5010 : #define FD_ACCDB_SIZE_CACHE_CLAIM_BIT (1U<<29)
     268        2886 : #define FD_ACCDB_SIZE_PD_WRITE_BIT    (1U<<28)
     269      281745 : #define FD_ACCDB_SIZE_MASK            ((1U<<28)-1U)
     270      106095 : #define FD_ACCDB_SIZE_PACK(sz,exec)   ((uint)(sz) | ((exec) ? FD_ACCDB_SIZE_EXEC_BIT : 0U))
     271      281745 : #define FD_ACCDB_SIZE_DATA(packed)    ((packed) & FD_ACCDB_SIZE_MASK)
     272       88929 : #define FD_ACCDB_SIZE_EXEC(packed)    (!!((packed) & FD_ACCDB_SIZE_EXEC_BIT))
     273        1845 : #define FD_ACCDB_SIZE_CACHE_VALID(p)  (!!((p) & FD_ACCDB_SIZE_CACHE_VALID_BIT))
     274          24 : #define FD_ACCDB_SIZE_CACHE_CLAIM(p)  (!!((p) & FD_ACCDB_SIZE_CACHE_CLAIM_BIT))
     275         120 : #define FD_ACCDB_SIZE_PD_WRITE(p)     (!!((p) & FD_ACCDB_SIZE_PD_WRITE_BIT))
     276             : 
     277             : FD_STATIC_ASSERT( (10UL<<20) < (1UL<<28), pd_write_bit_collides_with_len );
     278             : 
     279             : static inline ulong
     280        1491 : fd_accdb_acc_offset( fd_accdb_accmeta_t const * acc ) {
     281        1491 :   return acc->offset_fork & FD_ACCDB_OFF_MASK;
     282        1491 : }
     283             : 
     284             : static inline ushort
     285       16374 : fd_accdb_acc_fork_id( fd_accdb_accmeta_t const * acc ) {
     286       16374 :   return (ushort)( acc->offset_fork >> FD_ACCDB_OFF_BITS );
     287       16374 : }
     288             : 
     289             : static inline ulong
     290             : fd_accdb_acc_pack_offset_fork( ulong  offset,
     291      103269 :                                ushort fork_id ) {
     292      103269 :   return ( (ulong)fork_id << FD_ACCDB_OFF_BITS ) | ( offset & FD_ACCDB_OFF_MASK );
     293      103269 : }
     294             : 
     295             : /* fd_accdb_acc_xchg_offset atomically replaces the 48-bit offset
     296             :    portion of acc->offset_fork with new_offset while preserving the
     297             :    16-bit fork_id, and returns the previous 48-bit offset.  Uses a
     298             :    CAS loop so that concurrent compaction CAS and release-overwrite
     299             :    exchanges serialize correctly. */
     300             : 
     301             : static inline ulong
     302             : fd_accdb_acc_xchg_offset( fd_accdb_accmeta_t * acc,
     303        4623 :                            ulong               new_offset ) {
     304        4623 :   for(;;) {
     305        4623 :     ulong old_packed = FD_VOLATILE_CONST( acc->offset_fork );
     306        4623 :     ulong new_packed = ( old_packed & ~FD_ACCDB_OFF_MASK ) | ( new_offset & FD_ACCDB_OFF_MASK );
     307        4623 :     if( FD_LIKELY( FD_ATOMIC_CAS( &acc->offset_fork, old_packed, new_packed )==old_packed ) )
     308        4623 :       return old_packed & FD_ACCDB_OFF_MASK;
     309           0 :     FD_SPIN_PAUSE();
     310           0 :   }
     311        4623 : }
     312             : 
     313             : /* Packing helpers for the embedded acc cache index.  3 bits class
     314             :    in bits 31-29, 29 bits line index.  INVAL is the sentinel for
     315             :    "no cached location known".  FD_ACCDB_CACHE_LINE_MAX (defined in
     316             :    fd_accdb_cache.h) is the exclusive upper bound on representable
     317             :    line indices; per-class slot counts must not exceed it, or cidx
     318             :    values would alias. */
     319             : 
     320      197127 : #define FD_ACCDB_ACC_CIDX_IDX_MASK  ((uint)(FD_ACCDB_CACHE_LINE_MAX-1UL))
     321        3960 : #define FD_ACCDB_ACC_CIDX_INVAL     UINT_MAX
     322      106413 : #define FD_ACCDB_ACC_CIDX_PACK(c,i) ((uint)( ((uint)(c)<<FD_ACCDB_CACHE_LINE_BITS) | ((uint)(i) & FD_ACCDB_ACC_CIDX_IDX_MASK) ))
     323       92514 : #define FD_ACCDB_ACC_CIDX_CLASS(ci) ((ulong)((uint)(ci) >> FD_ACCDB_CACHE_LINE_BITS))
     324       90714 : #define FD_ACCDB_ACC_CIDX_IDX(ci)   ((ulong)((uint)(ci) & FD_ACCDB_ACC_CIDX_IDX_MASK))
     325             : 
     326             : #define POOL_NAME       acc_pool
     327      418272 : #define POOL_ELE_T      fd_accdb_accmeta_t
     328        1815 : #define POOL_NEXT       pool.next
     329             : #define POOL_IDX_T      uint
     330      970638 : #define POOL_IDX_WIDTH  32
     331             : #define POOL_IMPL_STYLE 0
     332             : #define POOL_LAZY       1
     333             : 
     334             : #include "../../util/tmpl/fd_pool_para.c"
     335             : 
     336             : struct fd_accdb_cache_line {
     337             :   fd_accdb_cache_key_t key;
     338             : 
     339             :   uint acc_idx;
     340             :   uint cache_idx;
     341             : 
     342             :   uint  refcnt;
     343             :   uchar persisted;
     344             :   uchar referenced;
     345             : 
     346             :   uint next;
     347             : 
     348             :   uchar owner[ 32UL ];
     349             : };
     350             : 
     351             : typedef struct fd_accdb_cache_line fd_accdb_cache_line_t;
     352             : 
     353             : typedef struct __attribute__((aligned(64))) { ulong val; } accdb_offset_t;
     354             : 
     355             : /* Partition offsets are packed into accdb_offset_t as:
     356             :      bits 63..51: partition pool index
     357             :      bits 50..0 : byte offset within the partition */
     358             : 
     359       11862 : #define FD_ACCDB_PARTITION_OFF_BITS 51UL
     360             : 
     361             : static FD_FN_CONST inline accdb_offset_t
     362             : accdb_offset( ulong partition_idx,
     363       11556 :               ulong partition_offset ) {
     364       11556 :   return (accdb_offset_t){ .val = (partition_idx<<FD_ACCDB_PARTITION_OFF_BITS) | partition_offset };
     365       11556 : }
     366             : 
     367             : static FD_FN_PURE inline ulong
     368         132 : packed_partition_idx( accdb_offset_t const * offset ) {
     369         132 :   return offset->val>>FD_ACCDB_PARTITION_OFF_BITS;
     370         132 : }
     371             : 
     372             : static FD_FN_PURE inline ulong
     373         174 : packed_partition_offset( accdb_offset_t const * offset ) {
     374         174 :   return offset->val & ((1UL<<FD_ACCDB_PARTITION_OFF_BITS)-1UL);
     375         174 : }
     376             : 
     377             : static FD_FN_PURE inline ulong
     378             : packed_partition_file_offset( accdb_offset_t const * offset,
     379          45 :                               ulong                  partition_sz ) {
     380          45 :    return (packed_partition_idx( offset )*partition_sz + packed_partition_offset( offset ));
     381          45 : }
     382             : 
     383             : /* Accounts are written to a tiered partition layout.  Layer 0 is the
     384             :    hot write head used by acquire/release (execution).  Layers 1..N-1
     385             :    are successively colder compaction tiers: partitions at layer K are
     386             :    compacted into layer K+1. */
     387             : 
     388     4158721 : #define FD_ACCDB_COMPACTION_LAYER_CNT (3UL)
     389             : 
     390             : /* Maximum number of concurrent joiners (tiles) that can publish an
     391             :    epoch in the accdb.  Each joiner claims a slot in the shared epoch
     392             :    array during fd_accdb_new.  Must be less than or equal to 256 so
     393             :    refcnt in cache lines can safely track the number of threads
     394             :    referencing each cache line without overflow.  With a uint refcnt
     395             :    field, 256 joiners is well within range. */
     396      986880 : #define FD_ACCDB_MAX_JOINERS (256UL)
     397             : 
     398             : /* EVICT_SENTINEL: stored in refcnt to indicate a cache line is being
     399             :    claimed by an eviction scan.  Any thread seeing this value must treat
     400             :    the line as unavailable. */
     401             : #define FD_ACCDB_EVICT_SENTINEL UINT_MAX
     402             : 
     403             : struct fd_accdb_shmem_private {
     404             :   int partition_lock  __attribute__((aligned(64)));
     405             : 
     406             :   /* Set non-zero by the snapin tile while a snapshot is being loaded.
     407             :      Suppresses compaction enqueue so the compaction tile does not race
     408             :      with bulk snapshot writes; fd_accdb_snapshot_load_end performs a
     409             :      one-shot sweep to enqueue any partitions that crossed the
     410             :      fragmentation threshold during the load. */
     411             :   int snapshot_loading;
     412             : 
     413             :   /* Set at construction (fd_accdb_shmem_new) when this validator
     414             :      supports bundles.  A bundle coalesces up to
     415             :      FD_ACCDB_MAX_TXN_PER_ACQUIRE transactions into one acquire, so when
     416             :      set, fd_accdb_acquire_inner permits pubkeys_cnt up to
     417             :      FD_ACCDB_MAX_ACQUIRE_CNT instead of the single-transaction limit
     418             :      FD_ACCDB_MAX_TX_ACCOUNT_LOCKS. */
     419             :   int bundle_enabled;
     420             : 
     421             :   /* Per-class CLOCK sweep position.  Atomically incremented by
     422             :      eviction scans (modulo cache_class_max[c]).  Each element is on
     423             :      its own cacheline to avoid false sharing between classes. */
     424             :   struct __attribute__((aligned(64))) { ulong val; } clock_hand[ FD_ACCDB_CACHE_CLASS_CNT ];
     425             : 
     426             :   /* Per-class CAS free list (Treiber stack) for fully-freed cache
     427             :      lines.  ver_top packs a 32-bit ABA version counter in bits
     428             :      63..32 and a uint pool index in bits 31..0.  UINT_MAX in the
     429             :      low 32 bits means empty. */
     430             :   struct __attribute__((aligned(64))) { ulong ver_top; } cache_free[ FD_ACCDB_CACHE_CLASS_CNT ];
     431             : 
     432             :   /* Per-class approximate depth of the CAS free list.  Atomically
     433             :      incremented on push, decremented on pop.  Used by the
     434             :      background pre-eviction loop to decide when to refill. */
     435             :   struct __attribute__((aligned(64))) { ulong val; } cache_free_cnt[ FD_ACCDB_CACHE_CLASS_CNT ];
     436             : 
     437             :   fd_accdb_fork_id_t root_fork_id;
     438             : 
     439             :   ulong seed;
     440             : 
     441             :   /* generation is a monotonically increasing counter assigned to each
     442             :      fork on creation.  When a fork is rooted, its pool slot (fork_id)
     443             :      is freed and may be recycled by a new fork, making fork_id in
     444             :      on-disk metadata useless for identifying entries from that freed
     445             :      fork.  But generation persists in disk metadata and is never
     446             :      recycled.
     447             : 
     448             :      Any rooted fork is by definition an ancestor of all live forks, so
     449             :      entries with generation <= root_fork->generation are
     450             :      unconditionally visible without consulting descends_set.  For
     451             :      entries with generation > root_fork->generation, the fork_id is
     452             :      still valid and descends_set is used to check ancestry.
     453             : 
     454             :      KEY INVARIANT: descends_set is ONLY consulted when generation >
     455             :      root_generation, which means the fork_id has NOT been rooted yet
     456             :      and its pool slot is still live.  This is what makes it safe for
     457             :      fork_slot_defer to eagerly clear descends_set bits for retired
     458             :      forks: rooted fork bits are dead (bypassed by the generation fast
     459             :      path), and purged fork bits were already 0 in all live forks'
     460             :      descends_sets (a purged fork is never an ancestor of a live fork).
     461             :      */
     462             :   uint generation;
     463             : 
     464             :   /* Lazy initial-allocation counter per size class.  Atomically
     465             :      incremented by acquire_cache_line (with undo on overflow). Each
     466             :      element is on its own cacheline to avoid false sharing between
     467             :      classes. */
     468             :   struct __attribute__((aligned(64))) { ulong val; } cache_class_init[ FD_ACCDB_CACHE_CLASS_CNT ];
     469             : 
     470             :   ulong cache_class_max[ FD_ACCDB_CACHE_CLASS_CNT ];
     471             : 
     472             :   /* Byte offsets from shmem base to the per-class cache regions. Each
     473             :      region is cache_class_max[c] * fd_accdb_cache_slot_sz[c] bytes,
     474             :      with each slot holding an fd_accdb_cache_line_t header followed by
     475             :      up to (fd_accdb_cache_slot_sz[c] - META_SZ) bytes of account data.
     476             :      */
     477             :   ulong cache_region_off[ FD_ACCDB_CACHE_CLASS_CNT ];
     478             : 
     479             :   /* Background pre-eviction watermarks (computed once in shmem_new).
     480             :      cache_free_target[c]: desired free-list depth for class c.
     481             :      cache_free_low_water[c]: trigger threshold ((target*3)/4). */
     482             :   ulong cache_free_target   [ FD_ACCDB_CACHE_CLASS_CNT ];
     483             :   ulong cache_free_low_water[ FD_ACCDB_CACHE_CLASS_CNT ];
     484             : 
     485             :   /* cache_class_used[i].val holds the number of reserved cache
     486             :      slots in size class i.  Acquire atomically increments; if the
     487             :      result exceeds cache_class_max[i] the reservation overflowed
     488             :      and the thread subtracts back and retries.  Release atomically
     489             :      decrements.  Each element is on its own cacheline to avoid
     490             :      false sharing between classes.  Invariant:
     491             :        used[i].val + available[i] == cache_class_max[i]
     492             :      at all times. */
     493             :   struct __attribute__((aligned(64))) { ulong val; } cache_class_used[ FD_ACCDB_CACHE_CLASS_CNT ];
     494             : 
     495             :   /* Per-layer write heads.  whead[0] is the hot (execution) write
     496             :      head, updated with atomic fetch-and-add by acquire/release
     497             :      threads.  whead[1..N-1] are compaction write heads, each
     498             :      single-writer (compaction tile only). */
     499             :   accdb_offset_t whead[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     500             :   int            has_partition[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     501             : 
     502             :   ulong partition_cnt;
     503             :   ulong partition_sz;
     504             :   ulong partition_max;
     505             : 
     506             :   ulong chain_cnt;
     507             :   ulong max_live_slots;
     508             :   ulong max_accounts;
     509             :   ulong max_account_writes_per_slot;
     510             : 
     511             :   /* Hard upper bound on concurrent joiners, set at construction.
     512             :      Used to determine whether cache_class_used tracking can be
     513             :      skipped for a given class (when max[c] >= MIN_RESERVED *
     514             :      joiner_cnt, every reservation succeeds trivially). */
     515             :   ulong joiner_cnt_max;
     516             : 
     517             :   /* Per-joiner worst-case cache reservation, set at construction.
     518             :      Used by fd_accdb_reset to replicate the cache_class_used sentinel
     519             :      logic from fd_accdb_shmem_new. */
     520             :   ulong cache_min_reserved;
     521             : 
     522             :   ulong partition_pool_off;
     523             : 
     524             :   /* compaction_dlist_off[k] is the byte offset (from shmem base) of
     525             :      the dlist sentinel for layer k.  Partitions at layer k that
     526             :      reach the freed-bytes threshold are enqueued here for compaction
     527             :      into layer k+1, or into layer k itself for the deepest layer. */
     528             :   ulong compaction_dlist_off[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     529             : 
     530             :   /* Epoch-based safe reclamation for compacted partitions.
     531             : 
     532             :      epoch is a monotonically increasing counter incremented by the
     533             :      compaction tile each time a partition finishes compaction.  The
     534             :      completed partition is tagged with the current epoch and pushed
     535             :      onto a deferred-free list instead of being released immediately.
     536             : 
     537             :      joiner_epochs[i] holds the epoch observed by joiner i at the start
     538             :      of its epoch-protected critical section, or ULONG_MAX when idle.
     539             :      The compaction tile scans this array to find the minimum observed
     540             :      epoch; any deferred partition tagged with an epoch strictly less
     541             :      than that minimum is safe to release, because every epoch-protected
     542             :      operation that could have snapshotted an offset into that partition
     543             :      has since exited its critical section.
     544             : 
     545             :      joiner_cnt is claimed via atomic fetch-and-add in fd_accdb_new
     546             :      and never decremented. */
     547             :   ulong epoch __attribute__((aligned(64)));
     548             : 
     549             :   /* Each joiner epoch is padded to a full cache line to prevent
     550             :      false sharing between joiners writing to adjacent slots. */
     551             :   struct __attribute__((aligned(64))) { ulong val; } joiner_epochs[ FD_ACCDB_MAX_JOINERS ];
     552             :   ulong joiner_cnt __attribute__((aligned(64)));
     553             :   ulong deferred_free_dlist_off;
     554             : 
     555             :   fd_accdb_shmem_metrics_t shmetrics[1];
     556             : 
     557             :   /* Command slot for T1 -> T2 offloading of advance_root / purge.
     558             :      Padded to its own cache line to avoid false sharing with the
     559             :      hot epoch / joiner_epochs fields above. */
     560             : 
     561     5270989 : #define FD_ACCDB_CMD_IDLE            (0U)
     562        1200 : #define FD_ACCDB_CMD_ADVANCE_ROOT    (1U)
     563          30 : #define FD_ACCDB_CMD_PURGE           (2U)
     564           6 : #define FD_ACCDB_CMD_CLEAR_DEFERRED  (3U)
     565           6 : #define FD_ACCDB_CMD_DRAIN_DEFERRED  (4U)
     566             : 
     567             :   uint   cmd_op       __attribute__((aligned(64))); /* FD_ACCDB_CMD_* */
     568             :   ushort cmd_fork_id;                               /* argument       */
     569             : 
     570             :   /* T2-only side buffer for deferred acc unlinks.  Holds the indices of
     571             :      accs that have been CAS-unlinked from their map chains in the
     572             :      current advance_root or purge call but cannot yet have pool.next
     573             :      written: a concurrent cold_load_acc may stomp it via the cache_idx
     574             :      union alias.  After wait_for_epoch_drain the list is materialized
     575             :      into pool.next links and released via acc_pool_release_chain.
     576             : 
     577             :      T2 is the sole writer (advance_root and purge both run on T2 via
     578             :      the cmd offload above), so cnt is plain.  Capacity is
     579             :      txn_max = max_live_slots * max_account_writes_per_slot, the same
     580             :      bound used to size txn_pool.  Stored as a byte offset from shmem
     581             :      base. */
     582             : 
     583             :   ulong deferred_acc_buf_off;
     584             :   ulong deferred_acc_buf_cnt;
     585             :   ulong deferred_acc_buf_max;
     586             :   ulong deferred_acc_epoch;
     587             : 
     588             :   acc_pool_shmem_t  acc_pool [1];
     589             :   fork_pool_shmem_t fork_pool[1];
     590             :   txn_pool_shmem_t  txn_pool [1];
     591             : 
     592             :   ulong magic; /* ==FD_ACCDB_SHMEM_MAGIC */
     593             : };
     594             : 
     595             : #endif /* HEADER_fd_src_flamenco_accdb_fd_accdb_private_h */

Generated by: LCOV version 1.14