LCOV - code coverage report
Current view: top level - util/spad - fd_spad.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 110 117 94.0 %
Date: 2024-11-13 11:58:15 Functions: 37 17472 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             : /* 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 to workaround legacy compiler issues. */
      45             : 
      46     5213700 : #define FD_SPAD_LG_ALIGN (7)
      47             : 
      48     6000129 : #define FD_SPAD_ALIGN (128)
      49             : 
      50             : #define FD_SPAD_FOOTPRINT(mem_max)                                    \
      51     5678331 :   FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT, \
      52     5678331 :     FD_SPAD_ALIGN, sizeof(fd_spad_t) ), /* metadata */                \
      53     5678331 :     FD_SPAD_ALIGN, (mem_max)         ), /* memory region */           \
      54     5678331 :     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    24015477 : #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    12638058 : #define FD_SPAD_ALLOC_ALIGN_DEFAULT (16UL)
      72             : 
      73             : /* Internal use only *************************************************/
      74             : 
      75             : /* Note: Details are exposed here to facilitate inlining of spad
      76             :    operations as they are typically used in performance critical
      77             :    contexts. */
      78             : 
      79      110238 : #define FD_SPAD_MAGIC (0xf17eda2ce759ad00UL) /* FIREDANCER SPAD version 0 */
      80             : 
      81             : /* spad internals */
      82             : 
      83             : struct __attribute__((aligned(FD_SPAD_ALIGN))) fd_spad_private {
      84             : 
      85             :   /* This point is FD_SPAD_ALIGN aligned */
      86             : 
      87             :   ulong magic; /* ==FD_SPAD_MAGIC */
      88             : 
      89             :   /* off[i] for i in [0,FD_SPAD_FRAME_MAX) gives the byte offset into
      90             :      the spad memory where allocations start for frame
      91             :      FD_SPAD_FRAME_MAX-1-i.  That is, off array usage grows toward 0
      92             :      such that off[i] for i in [0,frame_free) are not in use and
      93             :      [frame_free,FD_SPAD_FRAME_MAX) descripe the locations of current
      94             :      frames.  Typical bugs (e.g. pushing too many frames) here naturally
      95             :      clobber FD_SPAD_MAGIC (invalidating the spad) and then will clobber
      96             :      any guard region before the spad (invalidating the region) but will
      97             :      not clobber metadata or spad allocations themselves. */
      98             : 
      99             :   ulong off[ FD_SPAD_FRAME_MAX ];
     100             : 
     101             :   ulong frame_free; /* number of frames free, in [0,FD_SPAD_FRAME_MAX] */
     102             :   ulong mem_max;    /* byte size of the spad memory region */
     103             :   ulong mem_used;   /* number of spad memory bytes used, in [0,mem_max] */
     104             : 
     105             :   /* Padding to FD_SPAD_ALIGN here */
     106             : 
     107             :   /* "uchar mem[ mem_max ];" spad memory here.  Grows toward +inf such
     108             :      that bytes [0,mem_used) are currently allocated and bytes
     109             :      [mem_used,mem_max) are free.  As such, typical bugs (e.g. writing
     110             :      past the end of an allocation) naturally clobber any guard region
     111             :      after the structure (invalidate the region) but will not clobber
     112             :      the above metadata.  We are don't use a flexible array here due to
     113             :      lack of C++ support (sigh). */
     114             : 
     115             :   /* Padding to FD_SPAD_ALIGN here */
     116             : 
     117             : };
     118             : 
     119             : FD_PROTOTYPES_BEGIN
     120             : 
     121             : /* fd_spad_private_mem returns a pointer in the caller's local address
     122             :    space to the first byte of the spad's memory region.  Assumes spad is
     123             :    a current local join.  Lifetime of the returned pointer is the
     124             :    lifetime of the join. */
     125             : 
     126             : FD_FN_CONST static inline uchar *
     127    10592406 : fd_spad_private_mem( fd_spad_t * spad ) {
     128    10592406 :   return (uchar *)(spad+1UL);
     129    10592406 : }
     130             : 
     131             : FD_PROTOTYPES_END
     132             : 
     133             : /* End internal use only *********************************************/
     134             : 
     135             : FD_PROTOTYPES_BEGIN
     136             : 
     137             : /* constructors */
     138             : 
     139             : /* fd_spad_reset pops all frames in use.  Assumes spad is a current
     140             :    local join.  On return, spad is not in a frame.  Fast O(1).  This
     141             :    declared here to avoid forward use by fd_spad_new.  */
     142             : 
     143             : static inline void
     144      110958 : fd_spad_reset( fd_spad_t * spad ) {
     145      110958 :   spad->frame_free = FD_SPAD_FRAME_MAX;
     146      110958 :   spad->mem_used   = 0UL;
     147      110958 : }
     148             : 
     149             : /* fd_spad_mem_max_max returns the largest mem_max possible for a spad
     150             :    that will fit into footprint bytes.  On success, returns the largest
     151             :    mem_max such that:
     152             : 
     153             :      fd_spad_footprint( mem_max ) == fd_ulong_align_dn( footprint, FD_SPAD_ALIGN )
     154             : 
     155             :    On failure, returns 0.  Reasons for failure include the footprint is
     156             :    too small, the resulting mem_max is too large or the actual mem_max
     157             :    that can be support is actually 0 (which is arguably not an error).
     158             :    This is provided for users that want to specify a spad in terms of
     159             :    its footprint rather than mem_max.  FIXME: consider compile time
     160             :    variant? */
     161             : 
     162             : FD_FN_CONST static inline ulong
     163     3000000 : fd_spad_mem_max_max( ulong footprint ) {
     164     3000000 :   ulong mem_max = fd_ulong_max( fd_ulong_align_dn( footprint, FD_SPAD_ALIGN ), sizeof(fd_spad_t) ) - sizeof(fd_spad_t);
     165     3000000 :   return fd_ulong_if( mem_max<=(1UL<<63), mem_max, 0UL );
     166     3000000 : }
     167             : 
     168             : /* fd_spad_{align,footprint} give the required alignment and footprint
     169             :    for a spad that can support up mem_max bytes total of allocations.
     170             :    fd_spad_align returns FD_SPAD_ALIGN.  fd_spad_footprint returns
     171             :    non-zero on success and 0 on failure (silent).  Reasons for failure
     172             :    include mem_max is too large. */
     173             : 
     174             : FD_FN_CONST static inline ulong
     175           0 : fd_spad_align( void ) {
     176           0 :   return FD_SPAD_ALIGN;
     177           0 : }
     178             : 
     179             : FD_FN_CONST static inline ulong
     180     5678331 : fd_spad_footprint( ulong mem_max ) {
     181     5678331 :   return fd_ulong_if( mem_max<=(1UL<<63), FD_SPAD_FOOTPRINT( mem_max ), 0UL );
     182     5678331 : }
     183             : 
     184             : /* fd_spad_new formats an unused memory region with the appropriate
     185             :    footprint and alignment into a spad.  shmem points in the caller's
     186             :    address space to the first byte of the region.  Returns shmem on
     187             :    success (silent) and NULL on failure.  Reasons for failure include
     188             :    NULL spad, misaligned spad and too large mem_max.  The caller is
     189             :    _not_ joined on return. */
     190             : 
     191             : static inline void *
     192             : fd_spad_new( void * shmem,
     193      110247 :              ulong  mem_max ) {
     194      110247 :   fd_spad_t * spad = (fd_spad_t *)shmem;
     195             : 
     196      110247 :   if( FD_UNLIKELY( !spad                                              ) ) return NULL;
     197      110244 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)spad, FD_SPAD_ALIGN ) ) ) return NULL;
     198      110241 :   if( FD_UNLIKELY( !fd_spad_footprint( mem_max )                      ) ) return NULL;
     199             : 
     200      110238 :   spad->mem_max = mem_max;
     201             : 
     202      110238 :   fd_spad_reset( spad);
     203             : 
     204      110238 :   FD_COMPILER_MFENCE();
     205      110238 :   FD_VOLATILE( spad->magic ) = FD_SPAD_MAGIC;
     206      110238 :   FD_COMPILER_MFENCE();
     207             : 
     208      110238 :   return spad;
     209      110241 : }
     210             : 
     211             : /* fd_spad_join joins a spad.  shspad points in the caller's address
     212             :    space to the first byte of the region containing the spad.  Returns a
     213             :    local handle of the join on success (this is not necessarily a simple
     214             :    cast of shspad) or NULL on failure (silent).  Reasons for failure
     215             :    include NULL spad, misaligned spad and shspad obviously does not
     216             :    contain an spad.  There is no practical limitation on the number of
     217             :    concurrent joins in a thread, process or system wide.*/
     218             : 
     219             : FD_FN_PURE static inline fd_spad_t *
     220      110247 : fd_spad_join( void * shspad ) {
     221      110247 :   fd_spad_t * spad = (fd_spad_t *)shspad;
     222             : 
     223      110247 :   if( FD_UNLIKELY( !spad                                              ) ) return NULL;
     224      110244 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)spad, FD_SPAD_ALIGN ) ) ) return NULL;
     225      110241 :   if( FD_UNLIKELY( spad->magic!=FD_SPAD_MAGIC                         ) ) return NULL;
     226             : 
     227      110238 :   return spad;
     228      110241 : }
     229             : 
     230             : /* fd_spad_leave leaves a spad join.  Returns a pointer in the caller's
     231             :    address space to the first byte of the region containing the spad on
     232             :    success (this is not necessarily a simple cast of spad) and NULL on
     233             :    failure (silent).  On success, the join is no longer current but the
     234             :    spad will continue to exist.  Implicitly cancels any in-progress
     235             :    prepare. */
     236             : 
     237             : FD_FN_CONST static inline void *
     238           6 : fd_spad_leave( fd_spad_t * spad ) {
     239           6 :   return (void *)spad;
     240           6 : }
     241             : 
     242             : /* fd_spad_delete unformats a memory region used as a spad.  shspad
     243             :    points in the caller's address space to the first byte of the region
     244             :    containing the spad.  Returns the shspad on success and NULL on
     245             :    failure (silent).  Reasons for failure include NULL shspad,
     246             :    misaligned shspad and shspad obviously does not contain an spad.
     247             :    Assumes there is nobody joined to the spad when it is deleted. */
     248             : 
     249             : static inline void *
     250          12 : fd_spad_delete( void * shspad ) {
     251          12 :   fd_spad_t * spad = (fd_spad_t *)shspad;
     252             : 
     253          12 :   if( FD_UNLIKELY( !spad                                              ) ) return NULL;
     254           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)spad, FD_SPAD_ALIGN ) ) ) return NULL;
     255           6 :   if( FD_UNLIKELY( spad->magic!=FD_SPAD_MAGIC                         ) ) return NULL;
     256             : 
     257           3 :   FD_COMPILER_MFENCE();
     258           3 :   FD_VOLATILE( spad->magic ) = 0UL;
     259           3 :   FD_COMPILER_MFENCE();
     260             : 
     261           3 :   return spad;
     262           6 : }
     263             : 
     264             : /* accessors */
     265             : 
     266             : /* fd_spad_frame_{max,used,free} return the {max,used,free} number of
     267             :    spad frames (used+free==max and max is always FD_SPAD_FRAME_MAX).
     268             :    Assumes spad is a current local join. */
     269             : 
     270           0 : FD_FN_CONST static inline ulong fd_spad_frame_max ( fd_spad_t const * spad ) { (void)spad; return FD_SPAD_FRAME_MAX;        }
     271    22127988 : FD_FN_PURE  static inline ulong fd_spad_frame_used( fd_spad_t const * spad ) { return FD_SPAD_FRAME_MAX - spad->frame_free; }
     272     4717407 : FD_FN_PURE  static inline ulong fd_spad_frame_free( fd_spad_t const * spad ) { return spad->frame_free;                     }
     273             : 
     274             : /* fd_spad_mem_{max,used,free} return the {max,used,free} number of
     275             :    bytes spad memory (used+free==max).  Assumes spad is a current local
     276             :    join. */
     277             : 
     278           3 : FD_FN_PURE static inline ulong fd_spad_mem_max ( fd_spad_t const * spad ) { return spad->mem_max;                  }
     279           3 : FD_FN_PURE static inline ulong fd_spad_mem_used( fd_spad_t const * spad ) { return spad->mem_used;                 }
     280           3 : FD_FN_PURE static inline ulong fd_spad_mem_free( fd_spad_t const * spad ) { return spad->mem_max - spad->mem_used; }
     281             : 
     282             : /* fd_spad_in_frame returns 1 if the spad is in a frame and 0 otherwise.
     283             :    Assumes spad is a current local join. */
     284             : 
     285           6 : FD_FN_PURE static inline int fd_spad_in_frame( fd_spad_t const * spad ) { return spad->frame_free<FD_SPAD_FRAME_MAX; }
     286             : 
     287             : /* fd_spad_alloc_max returns the maximum number of bytes with initial
     288             :    byte alignment of align that can currently be allocated / prepared
     289             :    (not including any in-progress prepare).  Assumes spad is a current
     290             :    local join and in a frame and align is an integer power of 2 in
     291             :    [1,FD_SPAD_ALIGN] or 0 (indicates to use
     292             :    FD_SPAD_ALLOC_ALIGN_DEFAULT). */
     293             : 
     294             : FD_FN_PURE static inline ulong
     295             : fd_spad_alloc_max( fd_spad_t const * spad,
     296     6521277 :                    ulong             align ) {
     297     6521277 :   align = fd_ulong_if( align>0UL, align, FD_SPAD_ALLOC_ALIGN_DEFAULT ); /* typically compile time */
     298     6521277 :   ulong off = fd_ulong_align_up( spad->mem_used, align );
     299     6521277 :   return fd_ulong_max( spad->mem_max, off ) - off;
     300     6521277 : }
     301             : 
     302             : /* fd_spad_frame_{lo,hi} returns the range of spad memory covered by the
     303             :    current frame (not including any in-progress prepare).  That is,
     304             :    [lo,hi) is the range of bytes in the caller's address space for the
     305             :    current frame.  Assumes spad is a current local join and in a frame.
     306             :    FIXME: consider const correct versions? */
     307             : 
     308             : FD_FN_PURE static inline void *
     309     2237814 : fd_spad_frame_lo( fd_spad_t * spad ) {
     310     2237814 :   return fd_spad_private_mem( spad ) + spad->off[ spad->frame_free ];
     311     2237814 : }
     312             : 
     313             : FD_FN_PURE static inline void *
     314     2237811 : fd_spad_frame_hi( fd_spad_t * spad ) {
     315     2237811 :   return fd_spad_private_mem( spad ) + spad->mem_used;
     316     2237811 : }
     317             : 
     318             : /* operations */
     319             : 
     320             : /* fd_spad_push creates a new spad frame and makes it the current frame.
     321             :    Assumes spad is a current local join with at least one frame free.
     322             :    Implicitly cancels any in-progress prepare.  On return, spad will be
     323             :    in a frame and not in a prepare.  Fast O(1). */
     324             : 
     325             : static inline void
     326     1629213 : fd_spad_push( fd_spad_t * spad ) {
     327     1629213 :   spad->off[ --spad->frame_free ] = spad->mem_used;
     328     1629213 : }
     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     1623216 : fd_spad_pop( fd_spad_t * spad ) {
     339     1623216 :   spad->mem_used = spad->off[ spad->frame_free++ ];
     340     1623216 : }
     341             : 
     342             : /* The construct:
     343             : 
     344             :      FD_SPAD_FRAME_BEGIN( spad )
     345             :        ... code block ...
     346             :      FD_SPAD_FRAME_END
     347             : 
     348             :    is exactly equivalent linguistically to:
     349             : 
     350             :      do
     351             :        ... code block ...
     352             :      while(0)
     353             : 
     354             :    but fd_spad_{push,pop} is automatically called when the code block is
     355             :    {entered,exited}.  This includes exiting via break or (ickily)
     356             :    return.  Assumes spad has at least one frame free when the code block
     357             :    is entered.  Fast O(1). */
     358             : 
     359             : static inline void
     360           0 : fd_spad_private_frame_end( fd_spad_t ** _spad ) { /* declared here to avoid a fd_spad_pop forward reference */
     361           0 :   fd_spad_pop( *_spad );
     362           0 : }
     363             : 
     364             : #define FD_SPAD_FRAME_BEGIN(spad) do {                                            \
     365             :   fd_spad_t * _spad __attribute__((cleanup(fd_spad_private_frame_end))) = (spad); \
     366             :   fd_spad_push( _spad );                                                          \
     367             :   do
     368             : 
     369             : #define FD_SPAD_FRAME_END while(0); } while(0)
     370             : 
     371             : /* fd_spad_alloc allocates sz bytes with alignment align from spad.
     372             :    Returns a pointer in the caller's address space to the first byte of
     373             :    the allocation (will be non-NULL with alignment align).  Assumes spad
     374             :    is a current local join and in a frame, align is an integer power of
     375             :    2 in [1,FD_SPAD_ALIGN] or 0 (indicates to use
     376             :    FD_SPAD_ALLOC_ALIGN_DEFAULT) and sz is in [0,alloc_max].  Implicitly
     377             :    cancels any in progress prepare.  On return, spad will be in a frame
     378             :    and not in a prepare.  Fast O(1).
     379             : 
     380             :    The lifetime of the returned region will be until the next pop or
     381             :    delete and of the returned pointer until pop, delete or leave.  The
     382             :    allocated region will be in the region backing the spad (e.g. if the
     383             :    spad is backed by wksp memory, the returned value will be a laddr
     384             :    that can be shared with threads in other processes using that wksp). */
     385             : 
     386             : static inline void *
     387             : fd_spad_alloc( fd_spad_t * spad,
     388             :                ulong       align,
     389     2202411 :                ulong       sz ) {
     390     2202411 :   align = fd_ulong_if( align>0UL, align, FD_SPAD_ALLOC_ALIGN_DEFAULT ); /* typically compile time */
     391     2202411 :   ulong   off = fd_ulong_align_up( spad->mem_used, align );
     392     2202411 :   uchar * buf = fd_spad_private_mem( spad ) + off;
     393     2202411 :   spad->mem_used = off + sz;
     394     2202411 :   return buf;
     395     2202411 : }
     396             : 
     397             : /* fd_spad_trim trims trims frame_hi to end at hi where hi is given the
     398             :    caller's local address space.  Assumes spad is a current local join
     399             :    in a frame and hi is in [frame_lo,frame_hi] (FIXME: consider
     400             :    supporting allowing trim to expand the frame to mem_hi?).  Implicitly
     401             :    cancels any in-progress prepare.  On return, spad will be in a frame
     402             :    with frame_hi==hi.  Fast O(1).
     403             : 
     404             :    This is mostly useful for reducing the size of the most recent
     405             :    fd_spad_alloc.  E.g. call fd_spad_alloc with an upper bound to the
     406             :    final size, populate the region by bumping the returned pointer and
     407             :    calling trim at the end to return whatever remains of the original
     408             :    allocation to the spad.  Alternatively could use
     409             :    prepare/publish/cancel semantics below.
     410             : 
     411             :    Further note that the most recent sequence allocations in a frame can
     412             :    be _completely_ undone (including alignment padding) by saving
     413             :    frame_hi before the first alloc and then calling trim after the last
     414             :    (and most recent) alloc with the saved value. */
     415             : 
     416             : static inline void
     417             : fd_spad_trim( fd_spad_t * spad,
     418     2237808 :               void *      hi ) {
     419     2237808 :   spad->mem_used = (ulong)hi - (ulong)fd_spad_private_mem( spad );
     420     2237808 : }
     421             : 
     422             : /* fd_spad_prepare starts preparing a spad allocation with alignment
     423             :    align that can be up to max bytes in size.  Returns a pointer in the
     424             :    caller's address space to the initial byte of the allocation (will be
     425             :    non-NULL with alignment align).  Assumes spad is a current local join
     426             :    in a frame, align is an integer power of 2 in [1,FD_SPAD_ALIGN] or 0
     427             :    (indicates to use FD_SPAD_ALLOC_ALIGN_DEFAULT) and max is in
     428             :    [0,alloc_max].  Implicitly cancels any in-progress prepare.  On
     429             :    return, spad will be in a frame and in a prepare.  Fast O(1).
     430             : 
     431             :    While in a prepare, the lifetime of the returned region and returned
     432             :    pointer to it will be until prepare, cancel, alloc, trim, push, pop,
     433             :    leave or delete.  The region will be in the region backing the spad
     434             :    (e.g. if the spad is backed by a wksp, the returned value will be a
     435             :    laddr for its lifetime that can be shared with threads in other
     436             :    processes using that wksp).
     437             : 
     438             :    On publication, the returned value will behave _exactly_ as if:
     439             : 
     440             :      fd_spad_alloc( spad, align, sz )
     441             : 
     442             :    was called here instead of prepare.  This is mostly useful for
     443             :    optimizing allocations whose final size isn't known up front (e.g.
     444             :    buffering of real time streaming data).  Alternatively, could use
     445             :    alloc/trim semantics above.  FIXME: consider removing the
     446             :    prepare/publish/cancel APIs as it simplifies this by eliminating the
     447             :    concept of an in-progress prepare and it doesn't provide much benefit
     448             :    over alloc/trim. */
     449             : 
     450             : static inline void *
     451             : fd_spad_prepare( fd_spad_t * spad,
     452             :                  ulong       align,
     453     1676562 :                  ulong       max ) {
     454     1676562 :   (void)max;
     455     1676562 :   align = fd_ulong_if( align>0UL, align, FD_SPAD_ALLOC_ALIGN_DEFAULT ); /* typically compile time */
     456     1676562 :   ulong   off = fd_ulong_align_up( spad->mem_used, align );
     457     1676562 :   uchar * buf = fd_spad_private_mem( spad ) + off;
     458     1676562 :   spad->mem_used = off;
     459     1676562 :   return buf;
     460     1676562 : }
     461             : 
     462             : /* fd_spad_cancel cancels the most recent prepare.  Assumes spad is a
     463             :    current local join and in a prepare.  On return, spad will be in a
     464             :    frame and not in a prepare.  Fast O(1).
     465             : 
     466             :    IMPORTANT SAFETY TIP!  This is currently equivalent to
     467             :    fd_spad_publish( spad, 0 ).  As such, any alignment padding done in
     468             :    prepare will still be allocated on return.
     469             : 
     470             :    FIXME: consider undoing prepare's align_up too?  This requires extra
     471             :    state.  And, if a common alignment is being used, as is often the
     472             :    case (e.g. FD_SPAD_ALLOC_ALIGN_DEFAULT), as is typically the case,
     473             :    the amount of alignment padding will be typically 0.  And is usually
     474             :    negligible in other cases.  And prepare/cancel/publish are not often
     475             :    used anyway.  On top of that, it is already possible to undo the
     476             :    align_up in a supported way via the frame_hi / trim mechanism
     477             :    described above.  So this probably isn't worthwhile. */
     478             : 
     479             : static inline void
     480      558702 : fd_spad_cancel( fd_spad_t * spad ) {
     481      558702 :   (void)spad;
     482      558702 : }
     483             : 
     484             : /* fd_spad_publish finishes the allocation started in the most recent
     485             :    prepare.  Assumes spad is a current local join and in a prepare and
     486             :    sz is in [0,prepare's max].  On return, spad will be in a frame and
     487             :    not in a prepare.  Fast O(1).  See publish for more details. */
     488             : 
     489             : static inline void
     490             : fd_spad_publish( fd_spad_t * spad,
     491     1117860 :                  ulong       sz ) {
     492     1117860 :   spad->mem_used += sz;
     493     1117860 : }
     494             : 
     495             : /* fd_spad_verify returns a negative integer error code if the spad is
     496             :    obiviously corrupt if not (logs details) and 0 otherwise.  Reasons
     497             :    for failure include spad is not a current local join and frame
     498             :    metadata is corrupt (bad frames_used, bad mem_max, bad mem_used,
     499             :    frames don't nest).  This can only be used if logging services are
     500             :    available. */
     501             : 
     502             : int
     503             : fd_spad_verify( fd_spad_t const * spad );
     504             : 
     505             : /* The debugging variants below do additional checking and will
     506             :    FD_LOG_CRIT (dumping core) if their input requirements are not
     507             :    satisfied (they all still assume spad is a current local join).  They
     508             :    can only be used if logging services are available. */
     509             : 
     510             : ulong  fd_spad_alloc_max_debug( fd_spad_t const * spad, ulong  align            );
     511             : void * fd_spad_frame_lo_debug ( fd_spad_t       * spad                          );
     512             : void * fd_spad_frame_hi_debug ( fd_spad_t       * spad                          );
     513             : void   fd_spad_push_debug     ( fd_spad_t       * spad                          );
     514             : void   fd_spad_pop_debug      ( fd_spad_t       * spad                          );
     515             : void * fd_spad_alloc_debug    ( fd_spad_t       * spad, ulong  align, ulong sz  );
     516             : void   fd_spad_trim_debug     ( fd_spad_t       * spad, void * hi               );
     517             : void * fd_spad_prepare_debug  ( fd_spad_t       * spad, ulong  align, ulong max );
     518             : void   fd_spad_cancel_debug   ( fd_spad_t       * spad                          );
     519             : void   fd_spad_publish_debug  ( fd_spad_t       * spad, ulong  sz               );
     520             : 
     521             : static inline void
     522     1582119 : fd_spad_private_frame_end_debug( fd_spad_t ** _spad ) {
     523     1582119 :   fd_spad_pop_debug( *_spad );
     524     1582119 : }
     525             : 
     526     1582119 : #define FD_SPAD_FRAME_BEGIN_DEBUG(spad) do {                                            \
     527     1582119 :   fd_spad_t * _spad __attribute__((cleanup(fd_spad_private_frame_end_debug))) = (spad); \
     528     1582119 :   fd_spad_push_debug( _spad );                                                          \
     529     1582119 :   do
     530             : 
     531     1582119 : #define FD_SPAD_FRAME_END_DEBUG while(0); } while(0)
     532             : 
     533             : FD_PROTOTYPES_END
     534             : 
     535             : #endif /* HEADER_fd_src_util_spad_fd_spad_h */

Generated by: LCOV version 1.14