LCOV - code coverage report
Current view: top level - util/spad - fd_spad.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 133 153 86.9 %
Date: 2025-11-06 04:46:49 Functions: 36 32604 0.1 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_spad_fd_spad_h
       2             : #define HEADER_fd_src_util_spad_fd_spad_h
       3             : 
       4             : /* APIs for high performance persistent inter-process shared scratch pad
       5             :    memories.  A spad as a scratch pad that behaves very much like a
       6             :    thread's stack:
       7             : 
       8             :    - Spad allocations are very fast O(1) assembly.
       9             : 
      10             :    - Spad allocations are grouped into a frames.
      11             : 
      12             :    - Frames are nested.
      13             : 
      14             :    - Pushing and popping frames are also very fast O(1) assembly.
      15             : 
      16             :    - All allocations in a frame are automatically freed when the frame
      17             :      is popped.
      18             : 
      19             :    Unlike a thread's stack, the most recent allocation can be trimmed,
      20             :    the most recent sequence of allocations be undone, operations on a
      21             :    spad can by done more than one thread, threads can operate on
      22             :    multiple spads and, if the spad is backed by a shared memory region
      23             :    (e.g. wksp), spad allocations can be shared with different processes.
      24             :    Also, it flexibly supports tight integration with real-time
      25             :    streaming, custom allocation alignments, programmatic usage queries,
      26             :    validation, and a large dynamic range of allocation sizes and
      27             :    alignments.  Further, the API can be changed at compile time to
      28             :    implementations with extra instrumentation for debugging and/or
      29             :    sanitization. */
      30             : 
      31             : #include "../bits/fd_bits.h"
      32             : 
      33             : /* FD_SPAD_{ALIGN,FOOTPRINT} give the alignment and footprint of a
      34             :    fd_spad_t.  ALIGN is an integer power of 2.  FOOTPRINT is a multiple
      35             :    of ALIGN.  mem_max is assumed to be at most 2^63 such that the result
      36             :    is guaranteed to never overflow a ulong.  These are provided to
      37             :    facilitate compile time declarations a fd_spad_t.  128 is natural
      38             :    alignment for x86 adjacent cache line prefetching and PCI-e device
      39             :    interfacing like NICs and GPUs (e.g warp size).  Other possible
      40             :    useful alignments here include 256 (recent x86 DRAM memory fetch),
      41             :    512 (direct IO) and 4096 (x86 normal pages size).
      42             : 
      43             :    FD_SPAD_LG_ALIGN is log2 FD_SPAD_ALIGN.  Note: FD_SPAD_ALIGN is
      44             :    declared explicitly to workaround legacy compiler issues. */
      45             : 
      46     5213700 : #define FD_SPAD_LG_ALIGN (7)
      47             : 
      48     9000006 : #define FD_SPAD_ALIGN (128)
      49             : 
      50             : #define FD_SPAD_FOOTPRINT(mem_max)                                    \
      51     5457738 :   FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT, \
      52     5457738 :     FD_SPAD_ALIGN, sizeof(fd_spad_t) ), /* metadata */                \
      53     5457738 :     FD_SPAD_ALIGN, (mem_max)         ), /* memory region */           \
      54     5457738 :     FD_SPAD_ALIGN )
      55             : 
      56             : /* A fd_spad_t * is an opaque handle of a scratch pad memory */
      57             : 
      58             : struct fd_spad_private;
      59             : typedef struct fd_spad_private fd_spad_t;
      60             : 
      61             : /* FD_SPAD_FRAME_MAX gives the maximum number of frames in a spad. */
      62             : 
      63    19815465 : #define FD_SPAD_FRAME_MAX (128UL)
      64             : 
      65             : /* FD_SPAD_ALLOC_ALIGN_DEFAULT gives the default alignment for spad
      66             :    allocations.  Must be an integer power of 2 in [1,FD_SPAD_ALIGN].  16
      67             :    is uint128 and SSE natural alignment.  Other possible useful
      68             :    alignments here include 8 (minimum on a 64-bit target malloc/free
      69             :    conformanc), 32 (AVX2) and 64 (cache line and AVX-512). */
      70             : 
      71    11555649 : #define FD_SPAD_ALLOC_ALIGN_DEFAULT (16UL)
      72             : 
      73             : /* Asserts that the default spad alignment is greater than or equal to
      74             :    the asan and msan alignment when DEEPASAN / MSAN is enabled. */
      75             : #ifdef FD_HAS_DEEPASAN
      76             : FD_STATIC_ASSERT( FD_SPAD_ALLOC_ALIGN_DEFAULT >= FD_ASAN_ALIGN,
      77             :                   "default spad alignment must be greater than or equal to asan alignment" );
      78             : #endif
      79             : 
      80             : #ifdef FD_HAS_MSAN
      81             : FD_STATIC_ASSERT( FD_SPAD_ALLOC_ALIGN_DEFAULT >= FD_MSAN_ALIGN,
      82             :                   "default spad alignment must be greater than or equal to msan alignment" );
      83             : #endif
      84             : 
      85             : /* Internal use only *************************************************/
      86             : 
      87             : /* Note: Details are exposed here to facilitate inlining of spad
      88             :    operations as they are typically used in performance critical
      89             :    contexts. */
      90             : 
      91           3 : #define FD_SPAD_MAGIC (0xf17eda2ce759ad00UL) /* FIREDANCER SPAD version 0 */
      92             : 
      93             : /* spad internals */
      94             : 
      95             : struct __attribute__((aligned(FD_SPAD_ALIGN))) fd_spad_private {
      96             : 
      97             :   /* This point is FD_SPAD_ALIGN aligned */
      98             : 
      99             :   ulong magic; /* ==FD_SPAD_MAGIC */
     100             : 
     101             :   /* off[i] for i in [0,FD_SPAD_FRAME_MAX) gives the byte offset into
     102             :      the spad memory where allocations start for frame
     103             :      FD_SPAD_FRAME_MAX-1-i.  That is, off array usage grows toward 0
     104             :      such that off[i] for i in [0,frame_free) are not in use and
     105             :      [frame_free,FD_SPAD_FRAME_MAX) descripe the locations of current
     106             :      frames.  Typical bugs (e.g. pushing too many frames) here naturally
     107             :      clobber FD_SPAD_MAGIC (invalidating the spad) and then will clobber
     108             :      any guard region before the spad (invalidating the region) but will
     109             :      not clobber metadata or spad allocations themselves. */
     110             : 
     111             :   ulong off[ FD_SPAD_FRAME_MAX ];
     112             : 
     113             :   ulong frame_free; /* number of frames free, in [0,FD_SPAD_FRAME_MAX] */
     114             :   ulong mem_max;    /* byte size of the spad memory region */
     115             :   ulong mem_used;   /* number of spad memory bytes used, in [0,mem_max] */
     116             : 
     117             : #if FD_SPAD_TRACK_USAGE
     118             :   ulong mem_wmark;
     119             : #endif
     120             : 
     121             :   /* Padding to FD_SPAD_ALIGN here */
     122             : 
     123             :   /* "uchar mem[ mem_max ];" spad memory here.  Grows toward +inf such
     124             :      that bytes [0,mem_used) are currently allocated and bytes
     125             :      [mem_used,mem_max) are free.  As such, typical bugs (e.g. writing
     126             :      past the end of an allocation) naturally clobber any guard region
     127             :      after the structure (invalidate the region) but will not clobber
     128             :      the above metadata.  We are don't use a flexible array here due to
     129             :      lack of C++ support (sigh). */
     130             : 
     131             :   /* Padding to FD_SPAD_ALIGN here */
     132             : 
     133             : };
     134             : 
     135             : FD_PROTOTYPES_BEGIN
     136             : 
     137             : /* fd_spad_private_mem returns a pointer in the caller's local address
     138             :    space to the first byte of the spad's memory region.  Assumes spad is
     139             :    a current local join.  Lifetime of the returned pointer is the
     140             :    lifetime of the join. */
     141             : 
     142             : FD_FN_CONST static inline uchar *
     143     9509997 : fd_spad_private_mem( fd_spad_t * spad ) {
     144     9509997 :   return (uchar *)(spad+1UL);
     145     9509997 : }
     146             : 
     147             : FD_PROTOTYPES_END
     148             : 
     149             : /* End internal use only *********************************************/
     150             : 
     151             : FD_PROTOTYPES_BEGIN
     152             : 
     153             : /* constructors */
     154             : 
     155             : /* fd_spad_reset pops all frames in use.  Assumes spad is a current
     156             :    local join.  On return, spad is not in a frame.  Fast O(1).  This
     157             :    declared here to avoid forward use by fd_spad_new.  */
     158             : 
     159             : static inline void
     160             : fd_spad_reset( fd_spad_t * spad );
     161             : 
     162             : /* fd_spad_mem_max_max returns the largest mem_max possible for a spad
     163             :    that will fit into footprint bytes.  On success, returns the largest
     164             :    mem_max such that:
     165             : 
     166             :      fd_spad_footprint( mem_max ) == fd_ulong_align_dn( footprint, FD_SPAD_ALIGN )
     167             : 
     168             :    On failure, returns 0.  Reasons for failure include the footprint is
     169             :    too small, the resulting mem_max is too large or the actual mem_max
     170             :    that can be support is actually 0 (which is arguably not an error).
     171             :    This is provided for users that want to specify a spad in terms of
     172             :    its footprint rather than mem_max.  FIXME: consider compile time
     173             :    variant? */
     174             : 
     175             : FD_FN_CONST static inline ulong
     176     3000000 : fd_spad_mem_max_max( ulong footprint ) {
     177     3000000 :   ulong mem_max = fd_ulong_max( fd_ulong_align_dn( footprint, FD_SPAD_ALIGN ), sizeof(fd_spad_t) ) - sizeof(fd_spad_t);
     178     3000000 :   return fd_ulong_if( mem_max<=(1UL<<63), mem_max, 0UL );
     179     3000000 : }
     180             : 
     181             : /* fd_spad_{align,footprint} give the required alignment and footprint
     182             :    for a spad that can support up mem_max bytes total of allocations.
     183             :    fd_spad_align returns FD_SPAD_ALIGN.  fd_spad_footprint returns
     184             :    non-zero on success and 0 on failure (silent).  Reasons for failure
     185             :    include mem_max is too large. */
     186             : 
     187             : FD_FN_CONST static inline ulong
     188     3000000 : fd_spad_align( void ) {
     189     3000000 :   return FD_SPAD_ALIGN;
     190     3000000 : }
     191             : 
     192             : FD_FN_CONST static inline ulong
     193     5457738 : fd_spad_footprint( ulong mem_max ) {
     194     5457738 :   return fd_ulong_if( mem_max<=(1UL<<63), FD_SPAD_FOOTPRINT( mem_max ), 0UL );
     195     5457738 : }
     196             : 
     197             : /* fd_spad_new formats an unused memory region with the appropriate
     198             :    footprint and alignment into a spad.  shmem points in the caller's
     199             :    address space to the first byte of the region.  Returns shmem on
     200             :    success (silent) and NULL on failure.  Reasons for failure include
     201             :    NULL spad, misaligned spad and too large mem_max.  The caller is
     202             :    _not_ joined on return. */
     203             : 
     204             : static inline void *
     205             : fd_spad_new( void * shmem,
     206          12 :              ulong  mem_max ) {
     207          12 :   fd_spad_t * spad = (fd_spad_t *)shmem;
     208             : 
     209          12 :   if( FD_UNLIKELY( !spad                                              ) ) return NULL;
     210           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)spad, FD_SPAD_ALIGN ) ) ) return NULL;
     211           6 :   if( FD_UNLIKELY( !fd_spad_footprint( mem_max )                      ) ) return NULL;
     212             : 
     213           3 :   spad->mem_max = mem_max;
     214             : 
     215           3 :   fd_spad_reset( spad );
     216             : 
     217             : #if FD_SPAD_TRACK_USAGE
     218             :   spad->mem_wmark = 0UL;
     219             : #endif
     220             : 
     221           3 :   FD_COMPILER_MFENCE();
     222           3 :   FD_VOLATILE( spad->magic ) = FD_SPAD_MAGIC;
     223           3 :   FD_COMPILER_MFENCE();
     224             : 
     225           3 :   return spad;
     226           6 : }
     227             : 
     228             : /* fd_spad_join joins a spad.  shspad points in the caller's address
     229             :    space to the first byte of the region containing the spad.  Returns a
     230             :    local handle of the join on success (this is not necessarily a simple
     231             :    cast of shspad) or NULL on failure (silent).  Reasons for failure
     232             :    include NULL spad, misaligned spad and shspad obviously does not
     233             :    contain an spad.  There is no practical limitation on the number of
     234             :    concurrent joins in a thread, process or system wide.*/
     235             : 
     236             : FD_FN_PURE static inline fd_spad_t *
     237          12 : fd_spad_join( void * shspad ) {
     238          12 :   fd_spad_t * spad = (fd_spad_t *)shspad;
     239             : 
     240          12 :   if( FD_UNLIKELY( !spad                                              ) ) return NULL;
     241           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)spad, FD_SPAD_ALIGN ) ) ) return NULL;
     242           6 :   if( FD_UNLIKELY( spad->magic!=FD_SPAD_MAGIC                         ) ) return NULL;
     243             : 
     244           3 :   return spad;
     245           6 : }
     246             : 
     247             : /* fd_spad_leave leaves a spad join.  Returns a pointer in the caller's
     248             :    address space to the first byte of the region containing the spad on
     249             :    success (this is not necessarily a simple cast of spad) and NULL on
     250             :    failure (silent).  On success, the join is no longer current but the
     251             :    spad will continue to exist.  Implicitly cancels any in-progress
     252             :    prepare. */
     253             : 
     254             : FD_FN_CONST static inline void *
     255           6 : fd_spad_leave( fd_spad_t * spad ) {
     256           6 :   return (void *)spad;
     257           6 : }
     258             : 
     259             : /* fd_spad_delete unformats a memory region used as a spad.  shspad
     260             :    points in the caller's address space to the first byte of the region
     261             :    containing the spad.  Returns the shspad on success and NULL on
     262             :    failure (silent).  Reasons for failure include NULL shspad,
     263             :    misaligned shspad and shspad obviously does not contain an spad.
     264             :    Assumes there is nobody joined to the spad when it is deleted. */
     265             : 
     266             : static inline void *
     267             : fd_spad_delete( void * shspad );
     268             : 
     269             : /* accessors */
     270             : 
     271             : /* fd_spad_frame_{max,used,free} return the {max,used,free} number of
     272             :    spad frames (used+free==max and max is always FD_SPAD_FRAME_MAX).
     273             :    Assumes spad is a current local join. */
     274             : 
     275       88914 : FD_FN_CONST static inline ulong fd_spad_frame_max ( fd_spad_t const * spad ) { (void)spad; return FD_SPAD_FRAME_MAX;        }
     276    17949297 : FD_FN_PURE  static inline ulong fd_spad_frame_used( fd_spad_t const * spad ) { return FD_SPAD_FRAME_MAX - spad->frame_free; }
     277     3144663 : FD_FN_PURE  static inline ulong fd_spad_frame_free( fd_spad_t const * spad ) { return spad->frame_free;                     }
     278             : 
     279             : /* fd_spad_mem_{max,used,free} return the {max,used,free} number of
     280             :    bytes spad memory (used+free==max).  Assumes spad is a current local
     281             :    join. */
     282             : 
     283           3 : FD_FN_PURE static inline ulong fd_spad_mem_max ( fd_spad_t const * spad ) { return spad->mem_max;                  }
     284           3 : FD_FN_PURE static inline ulong fd_spad_mem_used( fd_spad_t const * spad ) { return spad->mem_used;                 }
     285           3 : FD_FN_PURE static inline ulong fd_spad_mem_free( fd_spad_t const * spad ) { return spad->mem_max - spad->mem_used; }
     286             : 
     287             : #if FD_SPAD_TRACK_USAGE
     288             : FD_FN_PURE static inline ulong fd_spad_mem_wmark( fd_spad_t const * spad ) { return spad->mem_wmark; }
     289             : #endif
     290             : 
     291             : /* fd_spad_in_frame returns 1 if the spad is in a frame and 0 otherwise.
     292             :    Assumes spad is a current local join. */
     293             : 
     294           6 : FD_FN_PURE static inline int fd_spad_in_frame( fd_spad_t const * spad ) { return spad->frame_free<FD_SPAD_FRAME_MAX; }
     295             : 
     296             : /* operations */
     297             : /* fd_spad_alloc_max returns the maximum number of bytes with initial
     298             :    byte alignment of align that can currently be allocated / prepared
     299             :    (not including any in-progress prepare).  Assumes spad is a current
     300             :    local join and in a frame and align is an integer power of 2 in
     301             :    [1,FD_SPAD_ALIGN] or 0 (indicates to use
     302             :    FD_SPAD_ALLOC_ALIGN_DEFAULT). */
     303             : 
     304             : FD_FN_PURE static inline ulong
     305             : fd_spad_alloc_max( fd_spad_t const * spad,
     306             :                    ulong             align );
     307             : 
     308             : /* fd_spad_frame_{lo,hi} returns the range of spad memory covered by the
     309             :    current frame (not including any in-progress prepare).  That is,
     310             :    [lo,hi) is the range of bytes in the caller's address space for the
     311             :    current frame.  Assumes spad is a current local join and in a frame.
     312             :    FIXME: consider const correct versions? */
     313             : 
     314             : FD_FN_PURE static inline void *
     315             : fd_spad_frame_lo( fd_spad_t * spad );
     316             : 
     317             : FD_FN_PURE static inline void *
     318             : fd_spad_frame_hi( fd_spad_t * spad );
     319             : 
     320             : /* operations */
     321             : 
     322             : /* fd_spad_push creates a new spad frame and makes it the current frame.
     323             :    Assumes spad is a current local join with at least one frame free.
     324             :    Implicitly cancels any in-progress prepare.  On return, spad will be
     325             :    in a frame and not in a prepare.  Fast O(1). */
     326             : 
     327             : static inline void
     328             : fd_spad_push( fd_spad_t * spad );
     329             : 
     330             : /* fd_spad_pop destroys the current spad frame (which bulk frees all
     331             :    allocations made in that frame and cancels any in progress prepare)
     332             :    and (if applicable) makes the previous frame current.  Assumes spad
     333             :    is a current local join (in a frame).  On return, spad will not be in
     334             :    a prepare and, if there was a previous frame, spad will be in a frame
     335             :    and not otherwise.  Fast O(1). */
     336             : 
     337             : static inline void
     338             : fd_spad_pop( fd_spad_t * spad );
     339             : 
     340             : /* The construct:
     341             : 
     342             :      FD_SPAD_FRAME_BEGIN( spad )
     343             :        ... code block ...
     344             :      FD_SPAD_FRAME_END
     345             : 
     346             :    is exactly equivalent linguistically to:
     347             : 
     348             :      do
     349             :        ... code block ...
     350             :      while(0)
     351             : 
     352             :    but fd_spad_{push,pop} is automatically called when the code block is
     353             :    {entered,exited}.  This includes exiting via break or (ickily)
     354             :    return.  Assumes spad has at least one frame free when the code block
     355             :    is entered.  Fast O(1). */
     356             : 
     357             : static inline void
     358        9372 : fd_spad_private_frame_end( fd_spad_t ** _spad ) { /* declared here to avoid a fd_spad_pop forward reference */
     359        9372 :   fd_spad_pop( *_spad );
     360        9372 : }
     361             : 
     362        9372 : #define FD_SPAD_FRAME_BEGIN(spad) {                                               \
     363        9372 :   fd_spad_t * _spad __attribute__((cleanup(fd_spad_private_frame_end))) = (spad); \
     364        9372 :   fd_spad_push( _spad );                                                          \
     365        9372 :   {
     366             : 
     367        6303 : #define FD_SPAD_FRAME_END }}
     368             : 
     369             : /* fd_spad_alloc allocates sz bytes with alignment align from spad.
     370             :    Returns a pointer in the caller's address space to the first byte of
     371             :    the allocation (will be non-NULL with alignment align).  Assumes spad
     372             :    is a current local join and in a frame, align is an integer power of
     373             :    2 in [1,FD_SPAD_ALIGN] or 0 (indicates to use
     374             :    FD_SPAD_ALLOC_ALIGN_DEFAULT) and sz is in [0,alloc_max].  Implicitly
     375             :    cancels any in progress prepare.  On return, spad will be in a frame
     376             :    and not in a prepare.  Fast O(1).
     377             : 
     378             :    The lifetime of the returned region will be until the next pop or
     379             :    delete and of the returned pointer until pop, delete or leave.  The
     380             :    allocated region will be in the region backing the spad (e.g. if the
     381             :    spad is backed by wksp memory, the returned value will be a laddr
     382             :    that can be shared with threads in other processes using that wksp). */
     383             : 
     384             : static inline void *
     385             : fd_spad_alloc( fd_spad_t * spad,
     386             :                ulong       align,
     387             :                ulong       sz );
     388             : 
     389             : /* fd_spad_trim trims trims frame_hi to end at hi where hi is given the
     390             :    caller's local address space.  Assumes spad is a current local join
     391             :    in a frame and hi is in [frame_lo,frame_hi] (FIXME: consider
     392             :    supporting allowing trim to expand the frame to mem_hi?).  Implicitly
     393             :    cancels any in-progress prepare.  On return, spad will be in a frame
     394             :    with frame_hi==hi.  Fast O(1).
     395             : 
     396             :    This is mostly useful for reducing the size of the most recent
     397             :    fd_spad_alloc.  E.g. call fd_spad_alloc with an upper bound to the
     398             :    final size, populate the region by bumping the returned pointer and
     399             :    calling trim at the end to return whatever remains of the original
     400             :    allocation to the spad.  Alternatively could use
     401             :    prepare/publish/cancel semantics below.
     402             : 
     403             :    Further note that the most recent sequence allocations in a frame can
     404             :    be _completely_ undone (including alignment padding) by saving
     405             :    frame_hi before the first alloc and then calling trim after the last
     406             :    (and most recent) alloc with the saved value. */
     407             : 
     408             : static inline void
     409             : fd_spad_trim( fd_spad_t * spad,
     410             :               void *      hi );
     411             : 
     412             : /* fd_spad_prepare starts preparing a spad allocation with alignment
     413             :    align that can be up to max bytes in size.  Returns a pointer in the
     414             :    caller's address space to the initial byte of the allocation (will be
     415             :    non-NULL with alignment align).  Assumes spad is a current local join
     416             :    in a frame, align is an integer power of 2 in [1,FD_SPAD_ALIGN] or 0
     417             :    (indicates to use FD_SPAD_ALLOC_ALIGN_DEFAULT) and max is in
     418             :    [0,alloc_max].  Implicitly cancels any in-progress prepare.  On
     419             :    return, spad will be in a frame and in a prepare.  Fast O(1).
     420             : 
     421             :    While in a prepare, the lifetime of the returned region and returned
     422             :    pointer to it will be until prepare, cancel, alloc, trim, push, pop,
     423             :    leave or delete.  The region will be in the region backing the spad
     424             :    (e.g. if the spad is backed by a wksp, the returned value will be a
     425             :    laddr for its lifetime that can be shared with threads in other
     426             :    processes using that wksp).
     427             : 
     428             :    On publication, the returned value will behave _exactly_ as if:
     429             : 
     430             :      fd_spad_alloc( spad, align, sz )
     431             : 
     432             :    was called here instead of prepare.  This is mostly useful for
     433             :    optimizing allocations whose final size isn't known up front (e.g.
     434             :    buffering of real time streaming data).  Alternatively, could use
     435             :    alloc/trim semantics above.  FIXME: consider removing the
     436             :    prepare/publish/cancel APIs as it simplifies this by eliminating the
     437             :    concept of an in-progress prepare and it doesn't provide much benefit
     438             :    over alloc/trim. */
     439             : 
     440             : static inline void *
     441             : fd_spad_prepare( fd_spad_t * spad,
     442             :                  ulong       align,
     443             :                  ulong       max );
     444             : 
     445             : /* fd_spad_cancel cancels the most recent prepare.  Assumes spad is a
     446             :    current local join and in a prepare.  On return, spad will be in a
     447             :    frame and not in a prepare.  Fast O(1).
     448             : 
     449             :    IMPORTANT SAFETY TIP!  This is currently equivalent to
     450             :    fd_spad_publish( spad, 0 ).  As such, any alignment padding done in
     451             :    prepare will still be allocated on return.
     452             : 
     453             :    FIXME: consider undoing prepare's align_up too?  This requires extra
     454             :    state.  And, if a common alignment is being used, as is often the
     455             :    case (e.g. FD_SPAD_ALLOC_ALIGN_DEFAULT), as is typically the case,
     456             :    the amount of alignment padding will be typically 0.  And is usually
     457             :    negligible in other cases.  And prepare/cancel/publish are not often
     458             :    used anyway.  On top of that, it is already possible to undo the
     459             :    align_up in a supported way via the frame_hi / trim mechanism
     460             :    described above.  So this probably isn't worthwhile. */
     461             : 
     462             : static inline void
     463             : fd_spad_cancel( fd_spad_t * spad );
     464             : 
     465             : /* fd_spad_publish finishes the allocation started in the most recent
     466             :    prepare.  Assumes spad is a current local join and in a prepare and
     467             :    sz is in [0,prepare's max].  On return, spad will be in a frame and
     468             :    not in a prepare.  Fast O(1).  See publish for more details. */
     469             : 
     470             : static inline void
     471             : fd_spad_publish( fd_spad_t * spad,
     472             :                  ulong       sz );
     473             : 
     474             : /* fd_spad_verify returns a negative integer error code if the spad is
     475             :    obiviously corrupt if not (logs details) and 0 otherwise.  Reasons
     476             :    for failure include spad is not a current local join and frame
     477             :    metadata is corrupt (bad frames_used, bad mem_max, bad mem_used,
     478             :    frames don't nest).  This can only be used if logging services are
     479             :    available. */
     480             : 
     481             : int
     482             : fd_spad_verify( fd_spad_t const * spad );
     483             : 
     484             : /* The debugging variants below do additional checking and will
     485             :    FD_LOG_CRIT (dumping core) if their input requirements are not
     486             :    satisfied (they all still assume spad is a current local join).  They
     487             :    can only be used if logging services are available. */
     488             : 
     489             : void   fd_spad_reset_debug    ( fd_spad_t       * spad                          );
     490             : void * fd_spad_delete_debug   ( void            * shspad                        );
     491             : ulong  fd_spad_alloc_max_debug( fd_spad_t const * spad, ulong  align            );
     492             : void * fd_spad_frame_lo_debug ( fd_spad_t       * spad                          );
     493             : void * fd_spad_frame_hi_debug ( fd_spad_t       * spad                          );
     494             : void   fd_spad_push_debug     ( fd_spad_t       * spad                          );
     495             : void   fd_spad_pop_debug      ( fd_spad_t       * spad                          );
     496             : void * fd_spad_alloc_check    ( fd_spad_t       * spad, ulong  align, ulong sz  );
     497           0 : #define fd_spad_alloc_debug fd_spad_alloc_check
     498             : void   fd_spad_trim_debug     ( fd_spad_t       * spad, void * hi               );
     499             : void * fd_spad_prepare_debug  ( fd_spad_t       * spad, ulong  align, ulong max );
     500             : void   fd_spad_cancel_debug   ( fd_spad_t       * spad                          );
     501             : void   fd_spad_publish_debug  ( fd_spad_t       * spad, ulong  sz               );
     502             : 
     503             : /* The sanitizer variants below have additional logic to control memory
     504             :    poisoning in ASAN/DEEPASAN and MSAN builds. */
     505             : 
     506             : void   fd_spad_reset_sanitizer_impl    ( fd_spad_t       * spad                          );
     507             : void * fd_spad_delete_sanitizer_impl   ( void            * shspad                        );
     508             : ulong  fd_spad_alloc_max_sanitizer_impl( fd_spad_t const * spad, ulong  align            );
     509             : void * fd_spad_frame_lo_sanitizer_impl ( fd_spad_t       * spad                          );
     510             : void * fd_spad_frame_hi_sanitizer_impl ( fd_spad_t       * spad                          );
     511             : void   fd_spad_push_sanitizer_impl     ( fd_spad_t       * spad                          );
     512             : void   fd_spad_pop_sanitizer_impl      ( fd_spad_t       * spad                          );
     513             : void * fd_spad_alloc_sanitizer_impl    ( fd_spad_t       * spad, ulong  align, ulong sz  );
     514             : void   fd_spad_trim_sanitizer_impl     ( fd_spad_t       * spad, void * hi               );
     515             : void * fd_spad_prepare_sanitizer_impl  ( fd_spad_t       * spad, ulong  align, ulong max );
     516             : void   fd_spad_cancel_sanitizer_impl   ( fd_spad_t       * spad                          );
     517             : void   fd_spad_publish_sanitizer_impl  ( fd_spad_t       * spad, ulong  sz               );
     518             : 
     519             : /* fn implementations */
     520             : static inline void
     521         723 : fd_spad_reset_impl( fd_spad_t * spad ) {
     522         723 :   spad->frame_free = FD_SPAD_FRAME_MAX;
     523         723 :   spad->mem_used   = 0UL;
     524         723 : }
     525             : 
     526             : static inline void *
     527          12 : fd_spad_delete_impl( void * shspad ) {
     528          12 :   fd_spad_t * spad = (fd_spad_t *)shspad;
     529             : 
     530          12 :   if( FD_UNLIKELY( !spad                                              ) ) return NULL;
     531           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)spad, FD_SPAD_ALIGN ) ) ) return NULL;
     532           6 :   if( FD_UNLIKELY( spad->magic!=FD_SPAD_MAGIC                         ) ) return NULL;
     533             : 
     534           3 :   FD_COMPILER_MFENCE();
     535           3 :   FD_VOLATILE( spad->magic ) = 0UL;
     536           3 :   FD_COMPILER_MFENCE();
     537             : 
     538           3 :   return spad;
     539           6 : }
     540             : 
     541             : FD_FN_PURE static inline ulong
     542             : fd_spad_alloc_max_impl( fd_spad_t const * spad,
     543     6521277 :                         ulong             align ) {
     544     6521277 :   align = fd_ulong_if( align>0UL, align, FD_SPAD_ALLOC_ALIGN_DEFAULT ); /* typically compile time */
     545     6521277 :   ulong off = fd_ulong_align_up( spad->mem_used, align );
     546     6521277 :   return fd_ulong_max( spad->mem_max, off ) - off;
     547     6521277 : }
     548             : 
     549             : FD_FN_PURE static inline void *
     550     2237814 : fd_spad_frame_lo_impl( fd_spad_t * spad ) {
     551     2237814 :   return fd_spad_private_mem( spad ) + spad->off[ spad->frame_free ];
     552     2237814 : }
     553             : 
     554             : FD_FN_PURE static inline void *
     555     2237811 : fd_spad_frame_hi_impl( fd_spad_t * spad ) {
     556     2237811 :   return fd_spad_private_mem( spad ) + spad->mem_used;
     557     2237811 : }
     558             : 
     559             : static inline void
     560       56466 : fd_spad_push_impl( fd_spad_t * spad ) {
     561       56466 :   spad->off[ --spad->frame_free ] = spad->mem_used;
     562       56466 : }
     563             : 
     564             : static inline void
     565       50469 : fd_spad_pop_impl( fd_spad_t * spad ) {
     566       50469 :   spad->mem_used = spad->off[ spad->frame_free++ ];
     567       50469 : }
     568             : 
     569             : static inline void *
     570             : fd_spad_alloc_impl( fd_spad_t * spad,
     571             :                     ulong       align,
     572     1120002 :                     ulong       sz ) {
     573     1120002 :   align = fd_ulong_if( align>0UL, align, FD_SPAD_ALLOC_ALIGN_DEFAULT ); /* typically compile time */
     574     1120002 :   ulong   off = fd_ulong_align_up( spad->mem_used, align );
     575     1120002 :   uchar * buf = fd_spad_private_mem( spad ) + off;
     576     1120002 :   spad->mem_used = off + sz;
     577             : #if FD_SPAD_TRACK_USAGE
     578             :   if( FD_UNLIKELY( spad->mem_wmark < spad->mem_used ) ) {
     579             :     spad->mem_wmark = spad->mem_used;
     580             :   }
     581             : #endif
     582             : 
     583     1120002 :   return buf;
     584     1120002 : }
     585             : 
     586             : static inline void
     587             : fd_spad_trim_impl( fd_spad_t * spad,
     588     2237808 :               void *      hi ) {
     589     2237808 :   spad->mem_used = (ulong)hi - (ulong)fd_spad_private_mem( spad );
     590     2237808 : }
     591             : 
     592             : static inline void *
     593             : fd_spad_prepare_impl( fd_spad_t * spad,
     594             :                       ulong       align,
     595     1676562 :                       ulong       max ) {
     596     1676562 :   (void)max;
     597     1676562 :   align = fd_ulong_if( align>0UL, align, FD_SPAD_ALLOC_ALIGN_DEFAULT ); /* typically compile time */
     598     1676562 :   ulong   off = fd_ulong_align_up( spad->mem_used, align );
     599     1676562 :   uchar * buf = fd_spad_private_mem( spad ) + off;
     600     1676562 :   spad->mem_used = off;
     601     1676562 :   return buf;
     602     1676562 : }
     603             : 
     604             : static inline void
     605      558702 : fd_spad_cancel_impl( fd_spad_t * spad ) {
     606      558702 :   (void)spad;
     607      558702 : }
     608             : 
     609             : static inline void
     610             : fd_spad_publish_impl( fd_spad_t * spad,
     611     1117860 :                       ulong       sz ) {
     612     1117860 :   spad->mem_used += sz;
     613     1117860 : }
     614             : 
     615             : /* fn definitions */
     616             : #if defined(FD_SPAD_USE_HANDHOLDING)
     617           0 : #define SELECT_IMPL(fn) fn##_debug
     618             : #elif (FD_HAS_DEEPASAN || FD_HAS_MSAN)
     619             : #define SELECT_IMPL(fn) fn##_sanitizer_impl
     620             : #else
     621     8400147 : #define SELECT_IMPL(fn) fn##_impl
     622             : #endif
     623             : 
     624             : void
     625         723 : fd_spad_reset( fd_spad_t * spad ) {
     626         723 :   SELECT_IMPL(fd_spad_reset)(spad);
     627         723 : }
     628             : 
     629             : static inline void *
     630          12 : fd_spad_delete( void * shspad ) {
     631          12 :   return SELECT_IMPL(fd_spad_delete)(shspad);
     632          12 : }
     633             : 
     634             : FD_FN_PURE ulong
     635             : fd_spad_alloc_max( fd_spad_t const * spad,
     636     3914424 :                    ulong             align ) {
     637     3914424 :   return SELECT_IMPL(fd_spad_alloc_max)(spad, align);
     638     3914424 : }
     639             : 
     640             : FD_FN_PURE void *
     641     2237808 : fd_spad_frame_lo( fd_spad_t * spad ) {
     642     2237808 :   return SELECT_IMPL(fd_spad_frame_lo)(spad);
     643     2237808 : }
     644             : 
     645             : FD_FN_PURE void *
     646     2237808 : fd_spad_frame_hi( fd_spad_t * spad ) {
     647     2237808 :   return SELECT_IMPL(fd_spad_frame_hi)(spad);
     648     2237808 : }
     649             : 
     650             : void
     651           0 : fd_spad_push( fd_spad_t * spad ) {
     652           0 :   SELECT_IMPL(fd_spad_push)(spad);
     653           0 : }
     654             : 
     655             : void
     656        9372 : fd_spad_pop(fd_spad_t *spad) {
     657        9372 :   SELECT_IMPL(fd_spad_pop)(spad);
     658        9372 : }
     659             : 
     660             : void *
     661             : fd_spad_alloc( fd_spad_t * spad,
     662             :                ulong       align,
     663           0 :                ulong       sz ) {
     664           0 :   return SELECT_IMPL(fd_spad_alloc)(spad, align, sz);
     665           0 : }
     666             : 
     667             : void
     668             : fd_spad_trim( fd_spad_t * spad,
     669           0 :               void *      hi ) {
     670           0 :   SELECT_IMPL(fd_spad_trim)(spad, hi);
     671           0 : }
     672             : 
     673             : void *
     674             : fd_spad_prepare( fd_spad_t * spad,
     675             :                  ulong       align,
     676           0 :                  ulong       max ) {
     677           0 :   return SELECT_IMPL(fd_spad_prepare)(spad, align, max);
     678           0 : }
     679             : 
     680             : void
     681           0 : fd_spad_cancel(fd_spad_t *spad) {
     682           0 :   SELECT_IMPL(fd_spad_cancel)(spad);
     683           0 : }
     684             : 
     685             : void
     686             : fd_spad_publish( fd_spad_t * spad,
     687           0 :                  ulong       sz ) {
     688           0 :   SELECT_IMPL(fd_spad_publish)(spad, sz);
     689           0 : }
     690             : 
     691             : FD_PROTOTYPES_END
     692             : 
     693             : #endif /* HEADER_fd_src_util_spad_fd_spad_h */

Generated by: LCOV version 1.14