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

Generated by: LCOV version 1.14