LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 27 28 96.4 %
Date: 2026-09-17 04:28:31 Functions: 109 7532 1.4 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_wksp_fd_wksp_h
       2             : #define HEADER_fd_src_util_wksp_fd_wksp_h
       3             : 
       4             : #include "../scratch/fd_scratch.h"
       5             : 
       6             : /* opaque; the tpool API is in tpool/fd_tpool.h */
       7             : struct fd_tpool_private;
       8             : typedef struct fd_tpool_private fd_tpool_t;
       9             : 
      10             : /* API for creating NUMA-aware and TLB-efficient workspaces used for
      11             :    complex inter-thread and inter-process shared memory communication
      12             :    patterns.  fd must be booted to use the APIs in this module.
      13             : 
      14             :    For example, startup scripts could reserve some memory on each NUMA
      15             :    node backed by huge and gigantic pages:
      16             : 
      17             :      sudo bin/fd_shmem_cfg alloc   8 gigantic 0 \
      18             :                            alloc   8 gigantic 1 \
      19             :                            alloc 256     huge 0 \
      20             :                            alloc 256     huge 1
      21             : 
      22             :    and then some of this memory could be formatted into fd_wksp for each
      23             :    NUMA node:
      24             : 
      25             :      bin/fd_shmem_ctl new my-wksp-numa-0 1 gigantic 0 \
      26             :                       new my-wksp-numa-1 1 gigantic 1
      27             : 
      28             :    Then, at application startup, processes can join these fd_wksp and
      29             :    concurrently allocate memory from the desired NUMA nodes as
      30             :    necessary.  E.g.
      31             : 
      32             :      fd_wksp_t * wksp = fd_wksp_attach( "my-wksp-numa-0" ); // logs details on failure
      33             :      if( !fd_wksp ) ... handle attach failure ...;
      34             : 
      35             :      ulong gaddr = fd_wksp_alloc( wksp, align, sz ); // logs details on failure
      36             :      if( !gaddr ) ... handle alloc failure ...;
      37             : 
      38             :    The local address of a workspace global address can be found via:
      39             : 
      40             :      void * laddr = fd_wksp_laddr( wksp, gaddr ); // logs details on failure
      41             :      if( !laddr ) ... handle bad (wksp,gaddr) ...;
      42             : 
      43             :    and the global address of a workspace local address can be found via:
      44             : 
      45             :      ulong gaddr = fd_wksp_gaddr( wksp, laddr ); // logs details on failure
      46             :      if( !gaddr ) ... handle bad (wksp,laddr) ...;
      47             : 
      48             :    Allocations can be freed via:
      49             : 
      50             :      fd_wksp_free( wksp, gaddr );
      51             : 
      52             :    Any join can free any allocation regardless of who made it.
      53             : 
      54             :    When the application is done using a wksp, it should leave it.  The
      55             :    workspace will continue to exist (it just is no longer safe to access
      56             :    in the caller's address space).  E.g.
      57             : 
      58             :      fd_wksp_detach( wksp ); // logs details on failure
      59             : 
      60             :    Likewise, if the workspaces are no longer in use, they can be deleted
      61             :    via something like:
      62             : 
      63             :      bin/fd_wksp_ctl delete my-wksp-numa-0 \
      64             :                      delete my-wksp-numa-1
      65             : 
      66             :    All allocations can be freed via something like:
      67             : 
      68             :      bin/fd_wksp_ctl reset my-wksp-numa-0 \
      69             :                      reset my-wksp-numa-1
      70             : 
      71             :    or in code:
      72             : 
      73             :      fd_wksp_reset( wksp, seed ); // logs details on failure
      74             : 
      75             :    It is the caller's responsibility to ensure that previous allocations
      76             :    to the wksp are not in use.
      77             : 
      78             :    Note: while this presents "aligned_alloc" style API semantics, this
      79             :    is not designed to be algorithmically optimal, HPC implementation or
      80             :    efficient at doing lots of tiny allocations.  Rather it is designed
      81             :    to be akin to an "mmap" / "sbrk" style allocator of last resort, done
      82             :    rarely and then ideally at application startup (e.g. setting up
      83             :    datastructures at box startup or used in an interprocess lockfree
      84             :    allocator as a mmap replacement).
      85             : 
      86             :    Instead, this tries to keep wksp fragmentation low with low overhead
      87             :    and tight packing of larger size allocations (normal page size and
      88             :    up).  It further tries to proactively limit the risk of heap
      89             :    _metadata_ corruption (proactive intraworkspace heap application
      90             :    _data_ corruption prevention is not a goal though typical mechanisms
      91             :    for such are in _direct_ opposition to efficient use of TLB, low
      92             :    fragmentation and tight allocation packing).  It is quasi-lockfree
      93             :    such that a process _killed_ in the middle of a workspace operation
      94             :    will not prevent other processes from using the workspace but a
      95             :    process _stalled_ in the middle of a workspace operations can stall
      96             :    other applications waiting to use the workspace indefinitely.
      97             :    Operators can track down an errant process stalled in the middle of
      98             :    workspace operations and blocking other processes).  Likewise
      99             :    detailed usage and metadata integrity checking and repair can be done
     100             :    via something like fd_wksp_ctl check / verify / rebuild / etc.
     101             :    Practically speaking, none of this really matters if usage occurs
     102             :    predominantly during application startup / shutdown.
     103             : 
     104             :    See below for more details. */
     105             : 
     106             : /* FD_WKSP_SUCCESS is used by various APIs to indicate an operation
     107             :    successfully completed.  This will be 0.  FD_WKSP_ERR_* gives a
     108             :    number of error codes used by fd_wksp APIs.  These will be negative
     109             :    integers. */
     110             : 
     111   253902579 : #define FD_WKSP_SUCCESS     (0)  /* Success */
     112          63 : #define FD_WKSP_ERR_INVAL   (-1) /* Failed due to obviously invalid inputs */
     113          78 : #define FD_WKSP_ERR_FAIL    (-2) /* Failed due to shared memory limitation */
     114    86050617 : #define FD_WKSP_ERR_CORRUPT (-3) /* Workspace memory corruption detected (potentially recoverable by rebuilding) */
     115             : 
     116             : /* FD_WKSP_{ALIGN,FOOTPRINT} describe the alignment and footprint of a
     117             :    fd_wksp_t.  ALIGN is a positive integer power of 2.  FOOTPRINT is a
     118             :    multiple of ALIGN.  FOOTPRINT assumes part_max and data_max are
     119             :    non-zero and small enough that the footprint will not overflow at
     120             :    most ULONG_MAX bytes.  These are provided to facilitate compile time
     121             :    declarations. */
     122             : 
     123        2133 : #define FD_WKSP_ALIGN (128UL)
     124             : #define FD_WKSP_FOOTPRINT( part_max, data_max )                                         \
     125             :   FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT, \
     126             :     FD_WKSP_ALIGN, 128UL           ), /* header */                                      \
     127             :     64UL,          64UL*(part_max) ), /* partition info */                              \
     128             :     1UL,           (data_max)+1UL  ), /* data region and footer */                      \
     129             :     FD_WKSP_ALIGN )                   /* tail padding */
     130             : 
     131             : /* FD_WKSP_ALIGN_DEFAULT gives the default alignments of a wksp
     132             :    allocation.  This is a positive integer power of two of at least 16
     133             :    (for malloc compatibility).  Additional details described in
     134             :    fd_wksp_alloc. */
     135             : 
     136    15396207 : #define FD_WKSP_ALIGN_DEFAULT (4096UL)
     137             : 
     138             : /* FD_WKSP_CSTR_MAX is the number of bytes maximum that can be in a wksp
     139             :    global address cstr. */
     140             : 
     141             : #define FD_WKSP_CSTR_MAX (FD_SHMEM_NAME_MAX + 21UL)
     142             : 
     143             : /* FD_WKSP_CHECKPT_STYLE_* specifies the streaming format to use for
     144             :    a workspace checkpoint.  These are non-zero.
     145             : 
     146             :      V1 - the stream will have extensive workspace metadata followed by
     147             :           the used workspace partitions.  No compression or hashing is
     148             :           done of the workspace partitions.
     149             : 
     150             :      V2 - similar to V1 in functionality but will be written such that
     151             :           checkpt and restore are parallelizable.
     152             : 
     153             :      V3 - This is actually V2 but compressed frames will be enabled.
     154             : 
     155             :      DEFAULT - the style to use when not specified by user.  0 indicates
     156             :      to use V3 if the target supports it and V2 if not. */
     157             : 
     158          63 : #define FD_WKSP_CHECKPT_STYLE_V1      (1)
     159         189 : #define FD_WKSP_CHECKPT_STYLE_V2      (2)
     160           0 : #define FD_WKSP_CHECKPT_STYLE_V3      (3)
     161             : 
     162             : #define FD_WKSP_CHECKPT_STYLE_DEFAULT (0)
     163             : 
     164             : #define FD_WKSP_CHECKPT_STYLE_RAW     FD_WKSP_CHECKPT_STYLE_V1 /* backward compat */
     165             : 
     166             : /* A fd_wksp_t * is an opaque handle of a workspace */
     167             : 
     168             : struct fd_wksp_private;
     169             : typedef struct fd_wksp_private fd_wksp_t;
     170             : 
     171             : /* A fd_wksp_usage_t is used to return workspace usage stats. */
     172             : 
     173             : struct fd_wksp_usage {
     174             :   ulong total_max;
     175             :   ulong total_cnt; ulong total_sz;
     176             :   ulong free_cnt;  ulong free_sz;
     177             :   ulong used_cnt;  ulong used_sz;
     178             : };
     179             : 
     180             : typedef struct fd_wksp_usage fd_wksp_usage_t;
     181             : 
     182             : FD_PROTOTYPES_BEGIN
     183             : 
     184             : /* Admin APIs *********************************************************/
     185             : 
     186             : /* It is rare to need to use the admin APIs directly (especially on a
     187             :    hosted system).  Recommend using the helper APIs below for most
     188             :    needs. */
     189             : 
     190             : /* Constructors */
     191             : 
     192             : /* fd_wksp_part_max_est computes an estimated maximum number of
     193             :    partitions for a workspace that needs to fit within footprint bytes
     194             :    and has sz_typical allocations typically.  Returns a positive value
     195             :    on success and 0 on failure.  Reasons for failure include footprint
     196             :    too small, sz_typical is 0 and sz_typical is so large that footprint
     197             :    has no room for metadata anyway.  Useful for determining how to pack
     198             :    a workspace tightly into a known footprint region. */
     199             : 
     200             : FD_FN_CONST ulong
     201             : fd_wksp_part_max_est( ulong footprint,
     202             :                       ulong sz_typical );
     203             : 
     204             : /* fd_wksp_data_max_est computes an estimated maximum data region size
     205             :    for footprint sized workspace with part_max partitions.  Returns a
     206             :    positive value on success and 0 on failure.  Reasons for failure
     207             :    include footprint is too small, part_max is 0, part_max is too large
     208             :    for under the hood implementation limitations or part_max is too
     209             :    large to have a non-zero sized data region.  Useful for determining
     210             :    how to pack a workspace into a known footprint region. */
     211             : 
     212             : FD_FN_CONST ulong
     213             : fd_wksp_data_max_est( ulong footprint,
     214             :                       ulong part_max );
     215             : 
     216             : /* fd_wksp_{align,footprint} give the required alignment and footprint
     217             :    for a workspace that can support up to part_max partitions and with a
     218             :    data region of data_max bytes.  fd_wksp_align returns FD_WKSP_ALIGN.
     219             :    fd_wksp_footprint(part_max,data_max) returns
     220             :    FD_WKSP_FOOTPRINT(part_max,data_max) on success and 0 on failure.
     221             :    Reasons for failure include zero part_max, part_max too large for
     222             :    this implementation, zero data_max, part_max/data_max requires a
     223             :    footprint that overflows a ULONG_MAX. */
     224             : 
     225             : FD_FN_CONST ulong
     226             : fd_wksp_align( void );
     227             : 
     228             : FD_FN_CONST ulong
     229             : fd_wksp_footprint( ulong part_max,
     230             :                    ulong data_max );
     231             : 
     232             : /* fd_wksp_new formats an unused memory region with the appropriate
     233             :    footprint and alignment mapped into the caller's address space at
     234             :    shmem into a wksp with given name (should be a valid fd_shmem name
     235             :    and will match the underlying shared memory region name / anonymous
     236             :    join for a wksp created via the shmem helpers below).  seed is the
     237             :    arbitrary value used to seed the heap priorities under the hood.
     238             :    Returns NULL on failure (logs details) or shmem on success.  The
     239             :    caller is _not_ joined on return. */
     240             : 
     241             : void *
     242             : fd_wksp_new( void *       shmem,
     243             :              char const * name,
     244             :              uint         seed,
     245             :              ulong        part_max,
     246             :              ulong        data_max );
     247             : 
     248             : /* fd_wksp_join joins a workspace.  shwksp is the location of the where
     249             :    the wksp has been mapped into the caller's address space.  Returns
     250             :    the local handle of the join on success or NULL on failure (logs
     251             :    details).  The caller can read / write memory in the joined workspace
     252             :    on return (a caller can do a read only join by mapping the shwksp
     253             :    into the local address as read only).  There is no practical
     254             :    limitation on the number of concurrent joins in a thread, process or
     255             :    system wide.*/
     256             : 
     257             : fd_wksp_t *
     258             : fd_wksp_join( void * shwksp );
     259             : 
     260             : /* fd_wksp_leave leaves a workspace.  Returns shwksp on success and NULL
     261             :    on failure (logs details).  The caller should not continue to read or
     262             :    write any memory for the join on return but the workspace will
     263             :    continue to exist. */
     264             : 
     265             : void *
     266             : fd_wksp_leave( fd_wksp_t * wksp );
     267             : 
     268             : /* fd_wksp_delete unformats a memory region used as a workspace.
     269             :    Returns the shmem on pointer on success and NULL on failure (logs
     270             :    details).  There should not be anybody joined to the workspace when
     271             :    it is deleted. */
     272             : 
     273             : void *
     274             : fd_wksp_delete( void * shwksp );
     275             : 
     276             : /* Accessors */
     277             : 
     278             : /* fd_wksp_name a cstr pointer to the wksp name (will point to a valid
     279             :    region name, e.g. strlen( name ) in [1,FD_SHMEM_NAME_MAX)).  Assumes
     280             :    wksp is a valid current join.  Lifetime of the returned string is the
     281             :    lifetime of the join.  The pointer value is const and the string
     282             :    pointed at is const for the lifetime of join.
     283             : 
     284             :    fd_wksp_seed returns the seed used at creation / most recent rebuild.
     285             :    Assumes wksp is a current local join.
     286             : 
     287             :    fd_wksp_{part_max,data_max} returns {part_max,data_max} used at
     288             :    creation.  Assumes wksp is a current local join.
     289             : 
     290             :    [fd_wksp_gaddr_lo,fd_wksp_gaddr_hi) is the range of valid wksp gaddr.
     291             :    lo is guaranteed to be non-zero.  hi = lo + data_max.  */
     292             : 
     293             : FD_FN_CONST char const * fd_wksp_name    ( fd_wksp_t const * wksp );
     294             : FD_FN_PURE  uint         fd_wksp_seed    ( fd_wksp_t const * wksp );
     295             : FD_FN_PURE  ulong        fd_wksp_part_max( fd_wksp_t const * wksp );
     296             : FD_FN_PURE  ulong        fd_wksp_data_max( fd_wksp_t const * wksp );
     297             : FD_FN_PURE  ulong        fd_wksp_gaddr_lo( fd_wksp_t const * wksp );
     298             : FD_FN_PURE  ulong        fd_wksp_gaddr_hi( fd_wksp_t const * wksp );
     299             : 
     300             : /* fd_wksp_owner returns the id of the thread group that was currently
     301             :    in a wksp operation (0 indicates the wksp was in the process of being
     302             :    constructed) or ULONG_MAX if there was no operation in progress on
     303             :    the workspace.  Assumes wksp is a current local join.  The value will
     304             :    correspond to some point of time between when the call was made and
     305             :    the call returned. */
     306             : 
     307             : ulong fd_wksp_owner( fd_wksp_t const * wksp );
     308             : 
     309             : /* Misc */
     310             : 
     311             : /* fd_wksp_strerror converts an FD_WKSP_SUCCESS / FD_WKSP_ERR_* code
     312             :    into a human readable cstr.  The lifetime of the returned pointer is
     313             :    infinite.  The returned pointer is always to a non-NULL cstr. */
     314             : 
     315             : FD_FN_CONST char const *
     316             : fd_wksp_strerror( int err );
     317             : 
     318             : /* fd_wksp_verify does extensive verification of wksp.  Returns
     319             :    FD_WKSP_SUCCESS (0) if there are no issues detected with the wksp or
     320             :    FD_WKSP_ERR_CORRUPT (negative) otherwise (logs details).  wksp is a
     321             :    current local join to a workspace.  This is used internally for
     322             :    verifying the integrity of a workspace if a caller detects in an
     323             :    operation that another caller died in the middle of a wksp operation.
     324             :    Users typically do not need to call this but it can be useful in
     325             :    debugging and testing.
     326             : 
     327             :    IMPORTANT SAFETY TIP!  This assumes there are no concurrent
     328             :    operations on wksp. */
     329             : 
     330             : int
     331             : fd_wksp_verify( fd_wksp_t * wksp );
     332             : 
     333             : /* fd_wksp_rebuilds a wksp.  This is used internally for rebuilding
     334             :    workspace when a caller detects that another caller died in the
     335             :    middle of an alloc or free and left the workspace in an inconsistent
     336             :    state.  Returns FD_WKSP_SUCCESS (0) if wksp was rebuilt successfully
     337             :    or a FD_WKSP_ERR_CORRUPT (negative) if it could not (logs details).
     338             : 
     339             :    Rebuilding operates under the principle of "do no harm".
     340             :    Specifically, rebuilding does not impact any completed wksp
     341             :    allocations (even when it fails).  It can either complete or rollback
     342             :    any partially complete alloc / free depends on far along the partial
     343             :    operation was.
     344             : 
     345             :    Rebuilding should be always possible outside of actual memory
     346             :    corruption or code bug.  The main reason for failure is overlapping
     347             :    allocations were discovered during the rebuild (which would either be
     348             :    caused by memory corruption or a bug).
     349             : 
     350             :    Users typically do not need to call this but it can be useful as a
     351             :    weak form of ASLR by changing up the seed.  This is not a fast
     352             :    operation.
     353             : 
     354             :    IMPORTANT SAFETY TIP!  This assumes there are no concurrent
     355             :    operations on wksp. */
     356             : 
     357             : int
     358             : fd_wksp_rebuild( fd_wksp_t * wksp,
     359             :                  uint        seed );
     360             : 
     361             : /* User APIs **********************************************************/
     362             : 
     363             : /* fd_wksp_laddr map a wksp global address (an address all joiners
     364             :    agree upon) to the caller's local address space.  Invalid global
     365             :    addresses and/or 0UL will map to NULL (logs details if invalid).
     366             :    Assumes wksp is a current local join (NULL returns NULL). */
     367             : 
     368             : void *
     369             : fd_wksp_laddr( fd_wksp_t const * wksp,
     370             :                ulong             gaddr );
     371             : 
     372             : /* fd_wksp_gaddr maps a wksp local address to the corresponding wksp
     373             :    global address (an address all joiners agree upon).  Invalid local
     374             :    addresses and/or NULL will map to 0UL (logs details if invalid).
     375             :    Assumes wksp is a current local join (NULL returns NULL). */
     376             : 
     377             : ulong
     378             : fd_wksp_gaddr( fd_wksp_t const * wksp,
     379             :                void const *      laddr );
     380             : 
     381             : /* fd_wksp_gaddr_fast converts a laddr into a gaddr under the assumption
     382             :    wksp is a current local join and laddr is non-NULL local address in
     383             :    the wksp. */
     384             : 
     385             : FD_FN_CONST static inline ulong
     386             : fd_wksp_gaddr_fast( fd_wksp_t const * wksp,
     387      259570 :                     void const *      laddr ) {
     388      259570 :   return (ulong)laddr - (ulong)wksp;
     389      259570 : }
     390             : 
     391             : /* fd_wksp_laddr_fast converts a gaddr into a laddr under the assumption
     392             :    wksp is a current local join and gaddr is non-NULL. */
     393             : 
     394             : FD_FN_CONST static inline void *
     395             : fd_wksp_laddr_fast( fd_wksp_t const * wksp,
     396    74406230 :                     ulong             gaddr ) {
     397    74406230 :   return (void *)((ulong)wksp + gaddr);
     398    74406230 : }
     399             : 
     400             : /* fd_wksp_alloc_at_least allocates at least sz bytes from wksp with
     401             :    an alignment of at least align (align must be a non-negative integer
     402             :    power-of-two or 0, which indicates to use the default alignment
     403             :    FD_WKSP_ALIGN_DEFAULT).  The allocation will be tagged with a
     404             :    positive value tag.  Returns the fd_wksp global address of the join
     405             :    on success and "NULL" (0UL) on failure (logs details).  A zero sz
     406             :    returns "NULL" (silent).  On return, [*lo,*hi) will contain the
     407             :    actually gaddr range allocated.  On success, [*lo,*hi) will overlap
     408             :    completely [ret,ret+sz) and ret will be aligned to requested
     409             :    alignment.  Assumes lo and hi are non-NULL.
     410             : 
     411             :    fd_wksp_alloc is a simple wrapper around fd_wksp_alloc_at_least for
     412             :    use when applications do not care about details of the actual
     413             :    allocated region.
     414             : 
     415             :    Note that fd_wksp_alloc / fd_wksp_free are not HPC implementations.
     416             :    Instead, these are designed to be akin to a mmap / sbrk allocator of
     417             :    "last resort" under the hood in other allocators like fd_alloc.  As
     418             :    such it prioritizes packing efficiency (best fit with arbitrary sizes
     419             :    and alignments allowed) over algorithmic efficiency (e.g.
     420             :    O(lg wksp_alloc_cnt) instead of O(1) like fd_alloc) and prioritize
     421             :    robustness against heap corruption (e.g. overrunning an allocation
     422             :    might corrupt the data in other allocations but will not corrupt the
     423             :    heap structure ... as the goal of this data structure is to encourage
     424             :    minimization of TLB usage, there is very little that can be done to
     425             :    proactively prevent intraworkspace interallocation data corruption).
     426             : 
     427             :    These operations are "quasi-lock-free".  Specifically, while they can
     428             :    suffer priority inversion due to a slow thread stalling other threads
     429             :    from using these operations, a process that is terminated in the
     430             :    middle of these operations leaves the wksp in a recoverable state.
     431             :    The only risk is the same risk generally from any application that
     432             :    uses persistent resources: applications that are terminated abruptly
     433             :    might leave allocations in the wksp that would have been freed had
     434             :    the application terminated normally.  As the allocator has no way to
     435             :    tell the difference between such allocations and allocations that are
     436             :    intended to outlive the application, it is the caller's
     437             :    responsibility to clean up such (allocation tagging can help greatly
     438             :    simplify this for users).  It would be possible to widen this API for
     439             :    applications to explicitly signal this intent and automatically clean
     440             :    up allocations not meant to outlive their creator but the general use
     441             :    here is expected to be long lived allocations.
     442             : 
     443             :    Priority inversion is not expected to be an issue practically as the
     444             :    expected use case is at app startup (some non-latency critical
     445             :    processes will do a handful of wksp operations to setup workspaces
     446             :    for applications on that box going forward and then the allocations
     447             :    will not be used again until the wksp is tore down / reset / etc).
     448             :    The remaining cases (e.g. a fine grained allocator like fd_alloc
     449             :    needs to procure more memory from the workspace) are expected to be
     450             :    rare enough that the O(lg N) costs still will be more than adequate.
     451             :    Note further that fd_alloc allows very fast interprocess allocations
     452             :    to be done by using a wksp as an allocator of last resort (in such,
     453             :    all allocations would be strictly lock free unless they needed to
     454             :    invoke this allocator, as is typically the case in other lock free
     455             :    allocators).
     456             : 
     457             :    Likewise, operations do extensive allocation metadata integrity
     458             :    checks to facilitate robust persistent usage.  If there is metadata
     459             :    data corruption detected (e.g. hardware fault, code corruption, etc),
     460             :    there are fsck-like APIs to rebuild wksp metadata.  Data integrity
     461             :    protection is more defined by the application.
     462             : 
     463             :    Tags are application specific.  They can allow manual and automated
     464             :    processes to do various debugging, diagnostics, analytics and garbage
     465             :    collection on a workspace (e.g. superblocks from a fd_alloc can be
     466             :    tagged specifically for that fd_alloc to allow memory leaks in
     467             :    general to be detected at program termination with no additional
     468             :    overheads and allow such leaks cleaned up via tagged frees).
     469             :    Notably, tags are wide enough to encode gaddrs.  This opens up the
     470             :    possibly for filesystem-like complex metadata operations.
     471             : 
     472             :    IMPORTANT!  align technically refers to the alignment in the wksp's
     473             :    global address space.  As such, wksp must be mmaped into each local
     474             :    address space with an alignment of at least the largest alignment the
     475             :    overall application intends to use.  Common practices automatically
     476             :    satisfy this (e.g. if wksp is backed by normal/huge/gigantic pages
     477             :    and only asks for alignments of at most a normal/huge/gigantic page
     478             :    sz, this constraint is automatically satisfied as fd_shmem_join needs
     479             :    to mmap wksp into the local address space with normal/huge/gigantic
     480             :    alignment anyway).  If doing more exotic things (e.g. backing wksp by
     481             :    normal pages but requiring much larger alignments), explicitly
     482             :    specifying the wksp virtual address location (e.g. in the
     483             :    fd_shmem_join call) might be necessary to satisfy this constraint.
     484             : 
     485             :    This implementation support arbitrary sz and align efficiently but
     486             :    each allocation will use up 1-3 wksp partitions to achieve this.  As
     487             :    these are a finite resources (and typically sized for a wksp that
     488             :    handles primarily larger allocations, like a fd_alloc huge
     489             :    superblock) and as there are allocators like fd_alloc that faster are
     490             :    algorithmically, lower overhead and lockfree O(1) for small sizes and
     491             :    alignment, it is strongly recommended to use this as an allocator of
     492             :    last resort and/or use this for larger chunkier allocations at
     493             :    application startup (e.g. sz + align >>> cache line).  An allocator
     494             :    like fd_alloc can then manage most allocations, falling back on this
     495             :    only when necessary. */
     496             : 
     497             : ulong
     498             : fd_wksp_alloc_at_least( fd_wksp_t * wksp,
     499             :                         ulong       align,
     500             :                         ulong       sz,
     501             :                         ulong       tag,
     502             :                         ulong *     lo,
     503             :                         ulong *     hi );
     504             : 
     505             : static inline ulong
     506             : fd_wksp_alloc( fd_wksp_t * wksp,
     507             :                ulong       align,
     508             :                ulong       sz,
     509        5496 :                ulong       tag ) {
     510        5496 :   ulong dummy[2];
     511        5496 :   return fd_wksp_alloc_at_least( wksp, align, sz, tag, dummy, dummy+1 );
     512        5496 : }
     513             : 
     514             : /* fd_wksp_free frees a wksp allocation.  gaddr is a global address that
     515             :    points to any byte in the allocation to free (i.e. can point to
     516             :    anything in of the gaddr range [*lo,*hi) returned by
     517             :    fd_wksp_alloc_at_least).  Logs details of any weirdness detected.
     518             :    Free of "NULL" (0UL) silently returns.  There are no restrictions on
     519             :    which join might free an allocation.  See note above other details. */
     520             : 
     521             : void
     522             : fd_wksp_free( fd_wksp_t * wksp,
     523             :               ulong       gaddr );
     524             : 
     525             : /* fd_wksp_tag returns the tag associated with an allocation.  gaddr
     526             :    is a wksp global address that points to any byte in the allocation.
     527             :    This is a fast O(lg wksp_alloc_cnt).  A return of 0 indicates that
     528             :    gaddr did not point into an allocation at some point in time between
     529             :    when this function was called until when it returned (this includes
     530             :    the cases when wksp is NULL and/or gaddr is 0).  This function is
     531             :    silent to facilitate integration with various analysis tools. */
     532             : 
     533             : ulong
     534             : fd_wksp_tag( fd_wksp_t * wksp,
     535             :              ulong       gaddr );
     536             : 
     537             : /* fd_wksp_tag_query queries the workspace for all partitions that match
     538             :    one of the given tags.  The tag array is indexed [0,tag_cnt).
     539             :    Returns info_cnt, the number of matching partitions.  Further, if
     540             :    info_max is non-zero, will return detailed information for the first
     541             :    (from low to high gaddr) min(info_cnt,info_max).  Returns 0 if no
     542             :    partitions match any tags.  If any wonkiness encountered (e.g. wksp
     543             :    is NULL, tag is not in positive, etc) returns 0 and logs details.
     544             :    This is O(wksp_alloc_cnt*tag_cnt) currently (but could be made
     545             :    O(wksp_alloc_cnt) with some additional work). */
     546             : 
     547             : struct fd_wksp_tag_query_info {
     548             :   ulong gaddr_lo; /* Partition covers workspace global addresses [gaddr_lo,gaddr_hi) */
     549             :   ulong gaddr_hi; /* 0<gaddr_lo<gaddr_hi */
     550             :   ulong tag;      /* Partition tag */
     551             : };
     552             : 
     553             : typedef struct fd_wksp_tag_query_info fd_wksp_tag_query_info_t;
     554             : 
     555             : ulong
     556             : fd_wksp_tag_query( fd_wksp_t *                wksp,
     557             :                    ulong const *              tag,
     558             :                    ulong                      tag_cnt,
     559             :                    fd_wksp_tag_query_info_t * info,
     560             :                    ulong                      info_max );
     561             : 
     562             : /* fd_wksp_tag_free frees all allocations in wksp that match one of the
     563             :    given tags.  The tag array is indexed [0,tag_cnt).  Logs details if
     564             :    any wonkiness encountered (e.g. wksp is NULL, tag is not in positive.
     565             :    This is O(wksp_alloc_cnt*tag_cnt) currently (but could be made
     566             :    O(wksp_alloc_cnt) with some additional work). */
     567             : 
     568             : void
     569             : fd_wksp_tag_free( fd_wksp_t *   wksp,
     570             :                   ulong const * tag,
     571             :                   ulong         tag_cnt );
     572             : 
     573             : /* fd_wksp_memset sets all bytes in a wksp allocation to character c.
     574             :    gaddr is a global address that points to any byte in the allocation
     575             :    (i.e. can point to anything in range returned by
     576             :    fd_wksp_alloc_at_least and will fill the whole range).  Logs details
     577             :    of any weirdness detected.  Clear of "NULL" (0UL) silently returns.
     578             :    Atomic with respect to other operations on this workspace. */
     579             : 
     580             : void
     581             : fd_wksp_memset( fd_wksp_t * wksp,
     582             :                 ulong       gaddr,
     583             :                 int         c );
     584             : 
     585             : /* fd_wksp_reset frees all allocations from the wksp.  Logs details on
     586             :    failure. */
     587             : 
     588             : void
     589             : fd_wksp_reset( fd_wksp_t * wksp,
     590             :                uint        seed );
     591             : 
     592             : /* fd_wksp_usage computes the wksp usage at some point in time between
     593             :    when the call was made and the call returned, populating the user
     594             :    provided usage structure with the result.  Always returns usage.
     595             : 
     596             :    wksp is a current local join to the workspace to compute usage.
     597             : 
     598             :    tag[tag_idx] for tag_idx in [0,tag_cnt) is an array of tags to
     599             :    compute the usage.  The order doesn't matter and, if a tag appears
     600             :    multiple times in the array, it will be counted once in the used
     601             :    stats.  A zero tag_cnt (potentially with a NULL tag) is fine
     602             :    (used_cnt,used_set for such will be 0,0).  A tag of 0 indicates to
     603             :    include free partitions in the used stats.
     604             : 
     605             :    total_max is the maximum partitions the wksp can have.  This will be
     606             :    positive (==part_max).
     607             : 
     608             :    total_sz is the number of bytes the wksp has available for
     609             :    partitioning (==data_max).  As the partitioning always covers the
     610             :    entire wksp, total_sz is constant for the lifetime of the wksp.
     611             : 
     612             :    total_cnt is the number of partitions the wksp currently has.  This
     613             :    will be in [1,total_max].
     614             : 
     615             :    free_cnt/sz is the number of free partitions / free bytes the wksp
     616             :    currently has.  A free partition has a tag of 0 and is currently
     617             :    available for splitting to satisfy the a future fd_wksp_alloc
     618             :    request.
     619             : 
     620             :    used_cnt/sz is the number of partitions / bytes used by wksp
     621             :    partitions whose tags match those in the provided tag set.
     622             : 
     623             :    This is O(wksp_alloc_cnt*tag_cnt) and will lock the wksp while
     624             :    running (and potentially block the caller if others are holding onto
     625             :    the lock).  So use in testing, etc.  Likewise, the precise meaning of
     626             :    the statistics computed by this API are dependent on the
     627             :    implementation details under the hood (that is do not be surprised if
     628             :    this API gets changed in the future). */
     629             : 
     630             : fd_wksp_usage_t *
     631             : fd_wksp_usage( fd_wksp_t *       wksp,
     632             :                ulong const *     tag,
     633             :                ulong             tag_cnt,
     634             :                fd_wksp_usage_t * usage );
     635             : 
     636             : /* shmem APIs *********************************************************/
     637             : 
     638             : /* fd_wksp_new_named creates a shared memory region named name and
     639             :    formats as a workspace.  Ignoring error trapping, this is a shorthand
     640             :    for:
     641             : 
     642             :      // Size the workspace to use all the memory
     643             :      ulong footprint = sum( sub_page_cnt[*] )*page_sz
     644             :      ulong part_max  = opt_part_max ? opt_part_max : fd_wksp_part_max_est( footprint, 64 KiB );
     645             :      ulong data_max  = fd_wksp_data_max_est( footprint, part_max );
     646             : 
     647             :      // Create the shared memory region and format as a workspace
     648             :      fd_shmem_create_multi( name, page_sz, sub_cnt, sub_page_cnt, sub_cpu_idx, mode );
     649             :      void * shmem = fd_shmem_join( name, FD_SHMEM_JOIN_MODE_READ_WRITE, NULL, NULL, NULL ) );
     650             :      fd_wksp_new( shmem, name, seed, part_max, data_max );
     651             :      fd_shmem_leave( shmem, NULL, NULL );
     652             : 
     653             :    The 64 KiB above is where fd_alloc currently transitions to directly
     654             :    allocating from the wksp.
     655             : 
     656             :    Returns FD_WKSP_SUCCESS (0) on success and an FD_WKSP_ERR_*
     657             :    (negative) on failure (logs details).  Reasons for failure include
     658             :    INVAL (user arguments obviously bad) and FAIL (could not procure or
     659             :    format the shared memory region). */
     660             : 
     661             : int
     662             : fd_wksp_new_named( char const *  name,
     663             :                    ulong         page_sz,
     664             :                    ulong         sub_cnt,
     665             :                    ulong const * sub_page_cnt,
     666             :                    ulong const * sub_cpu_idx,
     667             :                    ulong         mode,
     668             :                    uint          seed,
     669             :                    ulong         opt_part_max );
     670             : 
     671             : /* fd_wksp_delete_named deletes a workspace created with
     672             :    fd_wksp_new_named.  There should not be any other joins / attachments
     673             :    to wksp when this is called.  Returns FD_WKSP_SUCCESS (0) on success
     674             :    and FD_WKSP_ERR_* (negative) on failure (logs details). */
     675             : 
     676             : int
     677             : fd_wksp_delete_named( char const * name );
     678             : 
     679             : /* fd_wksp_new_anon creates a workspace local to this thread group that
     680             :    otherwise looks and behaves _exactly_ like a workspace shared between
     681             :    multiple thread groups on this host of the same name, TLB and NUMA
     682             :    properties.  Ignoring error trapping, this is a shorthand for:
     683             : 
     684             :      // Size the workspace to use all the memory
     685             :      ulong page_cnt  = sum( sub_page_cnt[*] );
     686             :      ulong footprint = page_cnt*page_sz;
     687             :      ulong part_max  = opt_part_max ? opt_part_max : fd_wksp_part_max_est( footprint, 64 KiB );
     688             :      ulong data_max  = fd_wksp_data_max_est( footprint, part_max );
     689             : 
     690             :      // Create the anonymous memory region and format as a workspace
     691             :      void * mem = fd_shmem_acquire_multi( page_sz, sub_cnt, sub_page_cnt, sub_cpu_idx );
     692             :      fd_wksp_t * wksp = fd_wksp_join( fd_wksp_new( mem, name, seed, part_max, data_max ) );
     693             :      fd_shmem_join_anonymous( name, FD_SHMEM_JOIN_MODE_READ_WRITE, wksp, mem, page_sz, page_cnt );
     694             : 
     695             :    There should be must no current shmem joins to name and the anonymous
     696             :    join will shadow any preexisting fd_shmem region with the same name
     697             :    in the calling thread group).  Returns the joined workspace on
     698             :    success and NULL on failure (logs details).  The final leave and
     699             :    delete to this workspace should be through fd_wksp_delete_anon. */
     700             : 
     701             : fd_wksp_t *
     702             : fd_wksp_new_anon( char const *  name,
     703             :                   ulong         page_sz,
     704             :                   ulong         sub_cnt,
     705             :                   ulong const * sub_page_cnt,
     706             :                   ulong const * sub_cpu_idx,
     707             :                   uint          seed,
     708             :                   ulong         opt_part_max );
     709             : 
     710             : /* fd_wksp_delete_anon deletes a workspace created with fd_wksp_new_anon
     711             :    There should not be any other joins / attachments to wksp when this
     712             :    is called.  This cannot fail from the caller's POV; logs details if
     713             :    any wonkiness is detected during the delete. */
     714             : 
     715             : void
     716             : fd_wksp_delete_anon( fd_wksp_t * wksp );
     717             : 
     718             : /* TODO: eliminate these legacy versions of the in favor of the above. */
     719             : 
     720             : static inline fd_wksp_t *
     721             : fd_wksp_new_anonymous( ulong         page_sz,
     722             :                        ulong         page_cnt,
     723             :                        ulong         cpu_idx,
     724             :                        char const *  name,
     725         141 :                        ulong         opt_part_max ) {
     726         141 :   return fd_wksp_new_anon( name, page_sz, 1UL, &page_cnt, &cpu_idx, 0U, opt_part_max );
     727         141 : }
     728             : 
     729          69 : static inline void fd_wksp_delete_anonymous( fd_wksp_t * wksp ) { fd_wksp_delete_anon( wksp ); }
     730             : 
     731             : /* fd_wksp_attach attach to the workspace held by the shared memory
     732             :    region with the given name.  If there are regions with the same name
     733             :    backed by different page sizes, defaults to the region backed by the
     734             :    largest page size.  Returns wksp on success and NULL on failure
     735             :    (details are logged).  Multiple attachments within are fine (all but
     736             :    the first attachment will be a reasonably fast O(1) call); all
     737             :    attachments in a process will use the same local fd_wksp_t handle.
     738             :    Every attach should be paired with a detach.  TODO: CONST-VARIANTS? */
     739             : 
     740             : fd_wksp_t *
     741             : fd_wksp_attach( char const * name );
     742             : 
     743             : /* fd_wksp_detach detaches from the given workspace.  All but the last
     744             :    detach should be a reasonably fast O(1) call.  Returns non-zero on
     745             :    failure. */
     746             : 
     747             : int
     748             : fd_wksp_detach( fd_wksp_t * wksp );
     749             : 
     750             : /* fd_wksp_containing maps a fd_wksp local addr to the corresponding
     751             :    fd_wksp local join.  Returns NULL if laddr does not appear to be from
     752             :    a locally joined fd_wksp.  Always silent such that this can be used
     753             :    to detect if a pointer is from a fd_wksp or not.  This is not a
     754             :    terribly fast call.  This API can only be used on laddrs in wksp are
     755             :    either named or anonymous workspaces. */
     756             : 
     757             : fd_wksp_t *
     758             : fd_wksp_containing( void const * laddr );
     759             : 
     760             : /* fd_wksp_alloc_laddr is the same as fd_wksp_alloc but returns a
     761             :    pointer in the caller's local address space if the allocation was
     762             :    successful (and NULL if not).  Ignoring error trapping, this is a
     763             :    shorthand for:
     764             : 
     765             :      fd_wksp_laddr( wksp, fd_wksp_alloc( wksp, align, sz, tag ) ) */
     766             : 
     767             : void *
     768             : fd_wksp_alloc_laddr( fd_wksp_t * wksp,
     769             :                      ulong       align,
     770             :                      ulong       sz,
     771             :                      ulong       tag );
     772             : 
     773             : /* fd_wksp_free_laddr is the same as fd_wksp_free but takes a pointer
     774             :    in the caller's local address space into a workspace allocation.
     775             :    Ignoring error trapping, this is a shorthand for:
     776             : 
     777             :      fd_wksp_t * wksp = fd_wksp_containing( laddr );
     778             :      fd_wksp_free( wksp, fd_wksp_gaddr( wksp, laddr ) );
     779             : 
     780             :    This API can only be used on laddrs in wksp are either named or
     781             :    anonymous workspaces. */
     782             : 
     783             : void
     784             : fd_wksp_free_laddr( void * laddr );
     785             : 
     786             : /* cstr helper APIs ***************************************************/
     787             : 
     788             : /* Overall, these are meant for use at application startup / shutdown
     789             :    and not in critical loops. */
     790             : 
     791             : /* fd_wksp_cstr prints the wksp global address gaddr into cstr as a
     792             :    [fd_wksp_name(wksp)]:[gaddr].  Caller promises that cstr has room for
     793             :    FD_WKSP_CSTR_MAX bytes.  Returns cstr on success and NULL on failure
     794             :    (logs details).  Reasons for failure include NULL wksp, gaddr not in
     795             :    the data region (or one past), NULL cstr. */
     796             : 
     797             : char *
     798             : fd_wksp_cstr( fd_wksp_t const * wksp,
     799             :               ulong             gaddr,
     800             :               char *            cstr );
     801             : 
     802             : /* fd_wksp_cstr_laddr is the same fd_wksp_cstr but takes a pointer in
     803             :    the caller's local address space to a wksp location.  Ignoring error
     804             :    trapping, this is a shorthand for:
     805             : 
     806             :      fd_wksp_t * wksp = fd_wksp_containing( laddr );
     807             :      return fd_wksp_cstr( wksp, fd_wksp_gaddr( wksp, laddr ), cstr );
     808             : 
     809             :    Returns NULL if laddr does not point strictly inside a workspace
     810             :    (logs details).  This API can only be used on laddrs in wksp are
     811             :    either named or anonymous workspaces. */
     812             : 
     813             : char *
     814             : fd_wksp_cstr_laddr( void const * laddr,
     815             :                     char *       cstr );
     816             : 
     817             : /* fd_wksp_cstr_alloc allocates sz bytes with alignment align from name
     818             :    or anonymous wksp with name.  align and sz have the exact same
     819             :    semantics as fd_wksp_alloc.  cstr must be non-NULL with space for up
     820             :    to FD_WKSP_CSTR_MAX bytes.
     821             : 
     822             :    Returns cstr on success and NULL on failure (logs details).  On
     823             :    success, cstr will contain a [name]:[gaddr] string suitable for use
     824             :    by fd_wksp_map and fd_wksp_cstr_free.  cstr will be untouched
     825             :    otherwise.  Ignoring error trapping, this is a shorthand for:
     826             : 
     827             :      fd_wksp_t * wksp  = fd_wksp_attach( name );
     828             :      ulong       gaddr = fd_wksp_alloc( wksp, align, sz );
     829             :      fd_wksp_detach( wksp );
     830             :      sprintf( cstr, "%s:%lu", name, gaddr );
     831             :      return cstr;
     832             : 
     833             :    As such, if doing many allocations from the same wksp, it is faster
     834             :    to do a fd_wksp_attach upfront, followed by the allocations and then
     835             :    a wksp detach (and faster still to use the advanced APIs to further
     836             :    amortize the fd_wksp_attach / fd_wksp_detach calls). */
     837             : 
     838             : char *
     839             : fd_wksp_cstr_alloc( char const * name,
     840             :                     ulong        align,
     841             :                     ulong        sz,
     842             :                     ulong        tag,
     843             :                     char *       cstr );
     844             : 
     845             : /* fd_wksp_cstr_free frees a wksp allocation specified by a cstr
     846             :    containing [name]:[gaddr].  Ignoring parsing and error trapping, this
     847             :    is a shorthand for:
     848             : 
     849             :       fd_wksp_t * wksp = fd_wksp_attach( name );
     850             :       fd_wksp_free( wksp, gaddr );
     851             :       fd_wksp_detach( wksp );
     852             : 
     853             :    As such, if doing many frees from the same wksp, it is faster to do a
     854             :    fd_wksp_attach upfront, followed by the frees and then a
     855             :    fd_wksp_detach (and faster still to use the advanced APIs to further
     856             :    amortize the fd_wksp_attach / fd_wksp_detach calls.) */
     857             : 
     858             : void
     859             : fd_wksp_cstr_free( char const * cstr );
     860             : 
     861             : /* fd_wksp_cstr_tag queries the tag of a wksp allocation specified by a
     862             :    cstr containing [name]:[gaddr].  Ignoring parsing and error trapping,
     863             :    this is a shorthand for:
     864             : 
     865             :       fd_wksp_t * wksp = fd_wksp_attach( name );
     866             :       ulong tag = fd_wksp_tag( wksp, gaddr );
     867             :       fd_wksp_detach( wksp );
     868             : 
     869             :    As such, if doing many queries on the same wksp, it is faster to do
     870             :    fd_wksp_attach upfront, followed by the queries and then a
     871             :    fd_wksp_detach (and faster still to use the advanced APIs to further
     872             :    amortize the fd_wksp_attach / fd_wksp_detach calls.) */
     873             : 
     874             : ulong
     875             : fd_wksp_cstr_tag( char const * cstr );
     876             : 
     877             : /* fd_wksp_cstr_memset memsets a wksp allocation specified by a cstr
     878             :    containing [name]:[gaddr] to c.  Ignoring parsing and error trapping,
     879             :    equivalent to:
     880             : 
     881             :       fd_wksp_t * wksp = fd_wksp_attach( name );
     882             :       fd_wksp_memset( wksp, gaddr, c );
     883             :       fd_wksp_detach( wksp );
     884             : 
     885             :    As such, if doing many memset in the same wksp, it is faster to do a
     886             :    fd_wksp_attach upfront, followed by the memsets and then a
     887             :    fd_wksp_detach (and faster still to use the advanced APIs to further
     888             :    amortize the fd_wksp_attach / fd_wksp_detach calls.) */
     889             : 
     890             : void
     891             : fd_wksp_cstr_memset( char const * cstr,
     892             :                      int          c );
     893             : 
     894             : /* fd_wksp_map returns a pointer in the caller's address space to
     895             :    the wksp allocation specified by a cstr containing [name]:[gaddr].
     896             :    [name] is the name of the shared memory region holding the wksp.
     897             :    [gaddr] is converted to a number via fd_cstr_to_ulong that should
     898             :    correspond to a valid non-NULL global address in that wksp.  Ignoring
     899             :    parsing, edge cases and error trapping, this is a shorthand for:
     900             : 
     901             :      fd_wksp_laddr( fd_wksp_attach( name ), gaddr )
     902             : 
     903             :    Returns non-NULL on successful (the lifetime of the returned pointer
     904             :    will be until fd_wksp_unmap is called on it).  Returns NULL and logs
     905             :    details on failure.
     906             : 
     907             :    fd_wksp_map is algorithmically efficient and reasonably low overhead
     908             :    (especially if is this not the first attachment to the wksp).
     909             : 
     910             :    TODO: consider const-correct variant? */
     911             : 
     912             : void *
     913             : fd_wksp_map( char const * cstr );
     914             : 
     915             : /* fd_wksp_unmap unmaps a pointer returned by fd_wksp_map, logs details
     916             :    if anything weird is detected.  Ignoring error trapping, this is a
     917             :    shorthand for:
     918             : 
     919             :      fd_wksp_detach( fd_wksp_containing( laddr ) )
     920             : 
     921             :    Undefined behavior if laddr is not currently mapped by fd_wksp_map.
     922             :    fd_wksp_unmap is not algorithmically efficient but practically still
     923             :    quite fast (especially if this is not the last attachment to wksp).
     924             :    This API can only be used on laddrs in wksp are either named or
     925             :    anonymous workspaces. */
     926             : 
     927             : void
     928             : fd_wksp_unmap( void const * laddr );
     929             : 
     930             : /* pod helper APIs ****************************************************/
     931             : 
     932             : /* Ignoring error trapping, fd_wksp_pod_attach( cstr ) is shorthand
     933             :    for:
     934             : 
     935             :      fd_pod_join( fd_wksp_map( cstr ) )
     936             : 
     937             :    Cannot fail from the caller's point of view (will terminate the
     938             :    thread group of the caller with a detailed FD_LOG_ERR message on
     939             :    failure.  Calls to fd_wksp_pod_attach should be paired with calls to
     940             :    fd_wksp_pod_detach when pod usage is done. */
     941             : 
     942             : uchar const *
     943             : fd_wksp_pod_attach( char const * cstr );
     944             : 
     945             : /* Ignoring error trapping, fd_wksp_pod_detach( pod ) is shorthand for:
     946             : 
     947             :      fd_wksp_unmap( fd_pod_leave( pod ) )
     948             : 
     949             :    Provided for symmetry with fd_wksp_pod_attach.  Cannot fail from the
     950             :    caller's point of view (will terminate the thread group of the caller
     951             :    with a detailed FD_LOG_ERR message on failure and will FD_LOG_WARNING
     952             :    if anything wonky occurs in the unmap under the hood). */
     953             : 
     954             : void
     955             : fd_wksp_pod_detach( uchar const * pod );
     956             : 
     957             : /* Ignoring error trapping, fd_wksp_pod_map( pod, path ) is shorthand
     958             :    for:
     959             : 
     960             :      fd_wksp_map( fd_pod_query_cstr( pod, path, NULL ) )
     961             : 
     962             :    Cannot fail from the caller's point of view (will terminate the
     963             :    thread group of the caller with detailed FD_LOG_ERR message on
     964             :    failure).  Calls to fd_wksp_pod_map should be paired with calls to
     965             :    fd_wksp_pod_unmap. */
     966             : 
     967             : void *
     968             : fd_wksp_pod_map( uchar const * pod,
     969             :                  char const *  path );
     970             : 
     971             : /* Ignoring error trapping, fd_wksp_pod_unmap( obj ) is shorthand for:
     972             : 
     973             :      fd_wksp_unmap( obj )
     974             : 
     975             :    Provided for symmetry with fd_wksp_pod_map.  Cannot fail from the
     976             :    caller's point of view (will terminate the thread group of the caller
     977             :    with a detailed FD_LOG_ERR message on failure and will FD_LOG_WARNING
     978             :    if anything wonky occurs in the unmap under the hood). */
     979             : 
     980             : void
     981             : fd_wksp_pod_unmap( void * obj );
     982             : 
     983             : /* io APIs ************************************************************/
     984             : 
     985             : /* fd_wksp_checkpt_tpool will write the wksp's state to a file using
     986             :    tpool threads [t0,t1).  Assumes the caller is thread t0 and threads
     987             :    (t0,t1) are available.  The file will be located at path with UNIX
     988             :    style permissions given by mode.  style specifies the checkpt style
     989             :    and should be a FD_WKSP_CHECKPT_STYLE_* value or 0 (0 indicates to
     990             :    use FD_WKSP_CHECKPT_STYLE_DEFAULT).  uinfo points to a cstr with
     991             :    optional additional user context (NULL will be treated as the empty
     992             :    string "" ... if the strlen is longer than 16384 bytes, the info will
     993             :    be truncated to a strlen of 16383).
     994             : 
     995             :    Returns FD_WKSP_SUCCESS (0) on success or a FD_WKSP_ERR_* on failure
     996             :    (logs details).  Reasons for failure include INVAL (NULL wksp, NULL
     997             :    path, bad mode, unsupported style), CORRUPT (wksp memory corruption
     998             :    detected), FAIL (fail already exists, I/O error).  On failure, this
     999             :    will make a best effort to clean up after any partially written
    1000             :    checkpt file.
    1001             : 
    1002             :    fd_wksp_checkpt is a convenience wrapper for serial checkpts. */
    1003             : 
    1004             : int
    1005             : fd_wksp_checkpt_tpool( fd_tpool_t * tpool,
    1006             :                        ulong        t0,
    1007             :                        ulong        t1,
    1008             :                        fd_wksp_t *  wksp,
    1009             :                        char const * path,
    1010             :                        ulong        mode,
    1011             :                        int          style,
    1012             :                        char const * uinfo );
    1013             : 
    1014             : static inline int
    1015             : fd_wksp_checkpt( fd_wksp_t *  wksp,
    1016             :                  char const * path,
    1017             :                  ulong        mode,
    1018             :                  int          style,
    1019          54 :                  char const * uinfo ) {
    1020          54 :   return fd_wksp_checkpt_tpool( NULL, 0UL, 1UL, wksp, path, mode, style, uinfo );
    1021          54 : }
    1022             : 
    1023             : /* fd_wksp_restore_tpool will replace all allocations in the current
    1024             :    workspace with the allocations from the checkpt at path.  The
    1025             :    restored workspace will use the given seed.  Tpool threads [t0,t1)
    1026             :    will be used for the restore.  Assumes the caller is thread t0 and
    1027             :    threads (t0,t1) are available.
    1028             : 
    1029             :    IMPORTANT!  It is okay for wksp to have a different size, backing
    1030             :    page sz and/or numa affinity than the original wksp.  The only
    1031             :    requirements are the wksp be able to support as many allocations as
    1032             :    are in the checkpt and that these partitions can be restored to their
    1033             :    original positions in wksp's global address space.  If wksp has
    1034             :    part_max in checkpt's [alloc_cnt,part_max] and a data_max>=checkpt's
    1035             :    data_max, this is guaranteed.  Likewise, the number and range of
    1036             :    threads used on restore does _not_ need to match the range used on
    1037             :    checkpt.
    1038             : 
    1039             :    Returns FD_WKSP_SUCCESS (0) on success or a FD_WKSP_ERR_* on failure
    1040             :    (logs details).  Reasons for failure include INVAL (NULL wksp, NULL
    1041             :    path), FAIL or CORRUPT (couldn't open checkpt, I/O error, checkpt
    1042             :    format error, incompatible wksp for checkpt, etc ... logs details).
    1043             :    For the INVAL and FAIL cases, the original workspace allocations was
    1044             :    untouched.  For the CORRUPT case, original workspace allocations were
    1045             :    removed because the checkpt issues were detected after the restore
    1046             :    process began (a best effort to reset wksp to the empty state was
    1047             :    done before return).
    1048             : 
    1049             :    fd_wksp_restore is a convenience wrapper for serial restores. */
    1050             : 
    1051             : int
    1052             : fd_wksp_restore_tpool( fd_tpool_t * tpool,
    1053             :                        ulong        t0,
    1054             :                        ulong        t1,
    1055             :                        fd_wksp_t *  wksp,
    1056             :                        char const * path,
    1057             :                        uint         seed );
    1058             : 
    1059             : static inline int
    1060             : fd_wksp_restore( fd_wksp_t *  wksp,
    1061             :                  char const * path,
    1062          63 :                  uint         seed ) {
    1063             :   return fd_wksp_restore_tpool( NULL, 0UL, 1UL, wksp, path, seed );
    1064          63 : }
    1065             : 
    1066             : /* fd_wksp_preview previews the wksp checkpt at path.  On success,
    1067             :    returns FD_WKSP_SUCCESS (0), path seems to contain a supported wksp
    1068             :    checkpt and, if opt_preview was non-NULL, *opt_preview will contain,
    1069             :    at a minimum, the info needed to create a new wksp with the same
    1070             :    parameters as the wksp at path.  On failure, returns a FD_WKSP_ERR
    1071             :    (negative, silent) and *_opt_preview is unchanged.  Returns for
    1072             :    failure include INVAL (NULL path), FAIL (unable to read checkpt
    1073             :    header at path), CORRUPT (the leading bytes at path don't appear to
    1074             :    be a wksp checkpt). */
    1075             : 
    1076             : struct fd_wksp_preview {
    1077             :   int   style;
    1078             :   uint  seed;
    1079             :   ulong part_max;
    1080             :   ulong data_max;
    1081             :   char  name[ FD_SHMEM_NAME_MAX ]; /* cstr holding the original wksp name */
    1082             : };
    1083             : 
    1084             : typedef struct fd_wksp_preview fd_wksp_preview_t;
    1085             : 
    1086             : int
    1087             : fd_wksp_preview( char const *        path,
    1088             :                  fd_wksp_preview_t * _opt_preview );
    1089             : 
    1090             : /* fd_wksp_printf pretty prints to fd (e.g. fileno(stdout)) information
    1091             :    about the wksp checkpt at path.  verbose specifies the verbosity
    1092             :    level.  Typical verbose levels are:
    1093             : 
    1094             :      <0 - do not print
    1095             :       0 - preview info
    1096             :       1 - verbose 0 + metadata
    1097             :       2 - verbose 1 + build and user info
    1098             :       3 - verbose 2 + partition summary info
    1099             :       4 - verbose 3 + individual allocated partition metdata
    1100             :      >4 - verbose 4 + hex dumps of allocated partition data
    1101             : 
    1102             :    but this can vary for different checkpt styles.  The return value has
    1103             :    the same interpretation as printf. */
    1104             : 
    1105             : int
    1106             : fd_wksp_printf( int          fd,
    1107             :                 char const * path,
    1108             :                 int          verbose );
    1109             : 
    1110             : FD_PROTOTYPES_END
    1111             : 
    1112             : #endif /* HEADER_fd_src_util_wksp_fd_wksp_h */

Generated by: LCOV version 1.14