LCOV - code coverage report
Current view: top level - util/spad - fd_spad.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 135 155 87.1 %
Date: 2025-03-20 12:08:36 Functions: 68 27840 0.2 %

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

Generated by: LCOV version 1.14