LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_clock.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 24 24 100.0 %
Date: 2026-09-17 04:28:31 Functions: 6 32 18.8 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_progcache_fd_progcache_clock_h
       2             : #define HEADER_fd_src_flamenco_progcache_fd_progcache_clock_h
       3             : 
       4             : /* fd_progcache_clock.h provides the cache eviction policy (CLOCK).
       5             : 
       6             :    A record is evicted when its size class is full: slots are drawn from that
       7             :    class's range, clearing the visited flag of any record that has it and
       8             :    evicting one that does not.  Access sets the flag.
       9             : 
      10             :    rec->state holds the flag, one byte per record including free ones:
      11             : 
      12             :      0                    free, or acquired and not yet published
      13             :      LOADING|MAPPED       published, program not yet loaded
      14             :      LIVE|MAPPED          holds a value and is reachable
      15             :      LIVE|VISITED|MAPPED  as above, recently accessed (CLOCK second chance)
      16             :      LOADING              unmapped mid-load; becomes a zombie when the load ends
      17             :      LIVE, LIVE|VISITED   zombie: unmapped; once detached and unheld, the next
      18             :                           eviction sweep hands its slot over (or a teardown
      19             :                           sweep frees it)
      20             : 
      21             :    It is the single source of truth for "is this record scannable" (rec->exists
      22             :    serves the different purpose of use-after-free detection on close). */
      23             : 
      24             : #include "fd_progcache_base.h"
      25             : #include "fd_progcache_rec.h"
      26             : 
      27    49611035 : #define FD_PROGCACHE_REC_LIVE    ((uchar)1)
      28    37450223 : #define FD_PROGCACHE_REC_VISITED ((uchar)2)
      29             : 
      30             : /* LOADING marks a record that is in the map but whose program is not loaded yet.  It
      31             :    serializes loaders: two tiles that miss on the same key do not both run
      32             :    fd_sbpf_program_load.  The first publishes the record LOADING and loads it; the rest
      33             :    find it in the map and wait.  It is not LIVE, so the CLOCK sweep steps over a record
      34             :    under load without a test of its own. */
      35             : 
      36     2751174 : #define FD_PROGCACHE_REC_LOADING ((uchar)4)
      37             : 
      38             : /* MAPPED marks a record reachable through the record map.  It is the only way to
      39             :    tell a rooted record, which stays mapped, from one a cancel or a sweep's claim
      40             :    unmapped: both have txn_idx==UINT_MAX. */
      41             : 
      42     1831184 : #define FD_PROGCACHE_REC_MAPPED  ((uchar)8)
      43             : 
      44             : FD_PROTOTYPES_BEGIN
      45             : 
      46             : /* The visited flag is a relaxed hint: CLOCK is approximate and a lost update is
      47             :    benign.  The load sentinel is not -- setting and clearing it are release stores,
      48             :    paired with the acquire in fd_prog_state_is_loading. */
      49             : 
      50             : /* fd_prog_state_load_begin marks the record at the given index LOADING|MAPPED
      51             :    ahead of fd_progcache_push inserting it; not LIVE.  Ended by fd_prog_state_touch. */
      52             : 
      53             : static inline void
      54             : fd_prog_state_load_begin( fd_progcache_rec_t * ele,
      55      917026 :                           ulong                rec_idx ) {
      56      917026 :   __atomic_store_n( &ele[ rec_idx ].state,
      57      917026 :                     (uchar)( FD_PROGCACHE_REC_LOADING|FD_PROGCACHE_REC_MAPPED ), __ATOMIC_RELEASE );
      58      917026 : }
      59             : 
      60             : /* fd_prog_state_touch marks the record at the given index as recently
      61             :    accessed, which makes it less likely to get evicted.  It also ends a load: the
      62             :    record becomes LIVE and the sentinel goes away in a single release store, so a
      63             :    waiter never observes both clear, and the loaded program is visible to anyone
      64             :    who sees LIVE. */
      65             : 
      66             : static inline void
      67             : fd_prog_state_touch( fd_progcache_rec_t * ele,
      68      917050 :                      ulong                rec_idx ) {
      69      917050 :   uchar cur = __atomic_load_n( &ele[ rec_idx ].state, __ATOMIC_RELAXED );
      70      917050 :   if( FD_UNLIKELY( cur & FD_PROGCACHE_REC_LOADING ) ) {
      71             :     /* LOADING is set and LIVE|VISITED clear here, so one xor performs the whole
      72             :        transition: a waiter never sees both LOADING and LIVE clear, and a
      73             :        concurrent unmap's MAPPED clear is not resurrected. */
      74      917026 :     __atomic_fetch_xor( &ele[ rec_idx ].state,
      75      917026 :                         (uchar)( FD_PROGCACHE_REC_LOADING|FD_PROGCACHE_REC_LIVE|FD_PROGCACHE_REC_VISITED ),
      76      917026 :                         __ATOMIC_RELEASE );
      77      917026 :     return;
      78      917026 :   }
      79          24 :   __atomic_fetch_or( &ele[ rec_idx ].state, (uchar)( FD_PROGCACHE_REC_LIVE|FD_PROGCACHE_REC_VISITED ), __ATOMIC_RELAXED );
      80          24 : }
      81             : 
      82             : /* fd_prog_state_is_loading returns 1 if a peer is still loading rec's program.  The
      83             :    acquire pairs with the release store in fd_prog_state_touch, so a caller that sees
      84             :    0 also sees the loaded program.  Deliberately not FD_FN_PURE: the value changes
      85             :    between calls, so a spin loop's calls must not be collapsed into one. */
      86             : 
      87             : static inline int
      88      917122 : fd_prog_state_is_loading( fd_progcache_rec_t const * rec ) {
      89      917122 :   return !!( __atomic_load_n( &rec->state, __ATOMIC_ACQUIRE ) & FD_PROGCACHE_REC_LOADING );
      90      917122 : }
      91             : 
      92             : /* fd_prog_state_clear marks the record at the given index as free / removed, run
      93             :    when the record is released or reinitialized.  The release keeps the preceding
      94             :    teardown ordered before the record reads as free. */
      95             : 
      96             : static inline void
      97             : fd_prog_state_clear( fd_progcache_rec_t * ele,
      98      921838 :                      ulong                rec_idx ) {
      99      921838 :   __atomic_store_n( &ele[ rec_idx ].state, (uchar)0, __ATOMIC_RELEASE );
     100      921838 : }
     101             : 
     102             : /* fd_prog_evict frees a record from the size class that fits sz
     103             :    and returns it read-locked and in-flight, as fd_progcache_rec_acquire does.
     104             :    Scans only that class's range, giving second chances and stepping over held
     105             :    records.  Returns NULL when the draws run out, the caller's cue to spill; that
     106             :    does not imply every record was held.  Release the return value with
     107             :    fd_progcache_rec_abandon. */
     108             : 
     109             : __attribute__((warn_unused_result))
     110             : fd_progcache_rec_t *
     111             : fd_prog_evict( fd_progcache_t * progcache,
     112             :                ulong            sz );
     113             : 
     114             : /* fd_prog_preevict tops up class class_idx's free list toward free_target: if
     115             :    the list is shorter, it runs one eviction sweep and releases the claimed
     116             :    victim's slot to the free list.  Evictions count into metrics (standard
     117             :    eviction counters) unless metrics is NULL.  Returns the number of slots
     118             :    freed (0 or 1).  Safe from any thread concurrently with everything;
     119             :    bounded by the sweep's 2*class_max draw budget. */
     120             : 
     121             : ulong
     122             : fd_prog_preevict( fd_progcache_join_t *    join,
     123             :                   fd_progcache_metrics_t * metrics,
     124             :                   ulong                    class_idx,
     125             :                   ulong                    free_target );
     126             : 
     127             : /* fd_progcache_housekeeping is one background maintenance tick: tops the next
     128             :    size class (round robin) toward 2 free slots via fd_prog_preevict, at most
     129             :    one slot per tick.  A zombie drawn by the sweep is handed over as-is (no
     130             :    second chance), so dead slots return to service without a separate
     131             :    collection pass.  Safe from any thread concurrently with everything. */
     132             : 
     133             : void
     134             : fd_progcache_housekeeping( fd_progcache_join_t *    join,
     135             :                            fd_progcache_metrics_t * metrics );
     136             : 
     137             : FD_PROTOTYPES_END
     138             : 
     139             : #endif /* HEADER_fd_src_flamenco_progcache_fd_progcache_clock_h */

Generated by: LCOV version 1.14