LCOV - code coverage report
Current view: top level - util/tmpl - fd_pool.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 146 148 98.6 %
Date: 2026-04-12 06:17:49 Functions: 794 15573 5.1 %

          Line data    Source code
       1             : /* Declare API for object pools of bounded run-time maximum size
       2             :    suitable for non-concurrent high performance persistent IPC usage.
       3             :    Typical usage:
       4             : 
       5             :      struct myele {
       6             :        ulong next; // Technically POOL_IDX_T POOL_NEXT, can go anywhere in struct,
       7             :                    // can be repurposed while acquired
       8             :                    // will be clobbered while in pool though
       9             :        ... user data ...
      10             :        ... structures with power-of-2 sizes have particularly good HPC
      11             :        ... Feng Shui
      12             :      }
      13             : 
      14             :      typedef struct myele myele_t;
      15             : 
      16             :      #define POOL_NAME mypool
      17             :      #define POOL_T    myele_t
      18             :      #include "tmpl/fd_pool.c"
      19             : 
      20             :    This will declare the following static inline APIs as a header only
      21             :    style library in the compilation unit:
      22             : 
      23             :      // align/footprint - Return the alignment/footprint required for a
      24             :      // memory region to be used as mypool that can hold up to max
      25             :      // elements.  footprint returns 0 if max is invalid (e.g. so large
      26             :      // that footprint would overflow ULONG_MAX or zero if mypool is
      27             :      // supposed to have a sentinel).
      28             :      //
      29             :      // new - Format a memory region pointed to by shmem into a mypool.
      30             :      // Assumes shmem points to a region with the required alignment and
      31             :      // footprint not in use by anything else.  Caller is not joined on
      32             :      // return.  Returns shmem on success or NULL on failure (e.g. shmem
      33             :      // or max are obviously bad).
      34             :      //
      35             :      // join - Join a mypool.  Assumes shpool points at a memory region
      36             :      // formatted as a mypool.  Returns a pointer in the caller's
      37             :      // address space to a memory region indexed [0,max) on success and
      38             :      // NULL on failure (e.g. shmem is obviously bad).  THIS IS NOT JUST
      39             :      // A SIMPLE CAST OF SHPOOL.
      40             :      //
      41             :      // leave - Leave a mypool.  Assumes join points to a current local
      42             :      // join.  Returns a pointer to the shared memory region the join on
      43             :      // success and NULL on failure.  THIS IS NOT JUST A SIMPLE CAST OF
      44             :      // JOIN.
      45             :      //
      46             :      // delete - Unformat a memory region used as mypool.  Assumes
      47             :      // shpool points to a formatted region with no current / future
      48             :      // joins.  Returns a pointer to the unformatted memory region.
      49             : 
      50             :      ulong     mypool_align    ( void      );
      51             :      ulong     mypool_footprint( ulong max );
      52             :      void *    mypool_new      ( void *    shmem, ulong max );
      53             :      myele_t * mypool_join     ( void *    shpool );
      54             :      void *    mypool_leave    ( myele_t * join   );
      55             :      void *    mypool_delete   ( void *    shpool );
      56             : 
      57             :      // Special values
      58             : 
      59             :      // All these assume join is a current local join to a mypool.  The
      60             :      // sentinel APIs are only created if POOL_SENTINEL is requested.
      61             : 
      62             :      ulong           mypool_idx_null          ( myele_t const * join ); // Index of the null element (not accessible)
      63             :                                                                         // infinite lifetime, ==(ulong)(POOL_IDX_T)~0UL
      64             :      myele_t *       mypool_ele_null          ( myele_t *       join ); // Location of the null element in the caller's address
      65             :                                                                         // space, infinite lifetime, ==NULL
      66             :      myele_t const * mypool_ele_null_const    ( myele_t const * join ); // Const correct version of above
      67             : 
      68             :      ulong           mypool_idx_sentinel      ( myele_t const * join ); // Index of the sentinel element (==0)
      69             :                                                                         // lifetime is the pool lifetime
      70             :      myele_t *       mypool_ele_sentinel      ( myele_t *       join ); // Location of the sentinel element in the caller's address
      71             :                                                                         // space, lifetime is the join lifetime (==join)
      72             :      myele_t const * mypool_ele_sentinel_const( myele_t const * join ); // Const correct version of above
      73             : 
      74             :      // Address space conversions
      75             : 
      76             :      int             mypool_idx_test ( myele_t const * join, ulong           idx ); // Returns 1 if idx is in [0,max) or is
      77             :                                                                                     // mypool_idx_null.  Returns zero otherwise.
      78             :      int             mypool_ele_test ( myele_t const * join, myele_t const * ele ); // Returns 1 if ele points to a pool ele or is
      79             :                                                                                     // NULL.  Returns zero otherwise.
      80             : 
      81             :      ulong           mypool_idx      ( myele_t const * join, myele_t const * ele ); // Returns idx associated with ele
      82             :                                                                                     // Assumes mypool_ele_test is 1
      83             :      myele_t *       mypool_ele      ( myele_t *       join, ulong           idx ); // Returns ele associated with idx
      84             :                                                                                     // Assumes mypool_idx_test is 1
      85             :                                                                                     // Lifetime is the local join
      86             :      myele_t const * mypool_ele_const( myele_t const * join, ulong           idx ); // Const correct version of above
      87             : 
      88             :      // Accessors
      89             : 
      90             :      ulong mypool_max ( myele_t const * join ); // Max elements in pool in [0,IDX_NULL], [1,IDX_NULL] if POOL_SENTINEL requested
      91             :      ulong mypool_free( myele_t const * join ); // Number of elements free, in [0,max]
      92             :      ulong mypool_used( myele_t const * join ); // Number of elements currently in use / acquired, in [0,max], includes sentinel
      93             :                                                 // if applicable, pool_free + pool_used == pool_max
      94             : 
      95             :      // Operations
      96             : 
      97             :      ulong     mypool_idx_acquire( myele_t * join                ); // Acquire an element from pool, assumes at least 1 free
      98             :                                                                     // Returns index of element, in [0,max) and, if applicable, not
      99             :                                                                     // sentinel index.  Lifetime is lesser of pool lifetime and
     100             :                                                                     // acquired element is released.  Will not return a currently
     101             :                                                                     // acquired element.
     102             :      void      mypool_idx_release( myele_t * join, ulong idx     ); // Release an element to pool by element idx, assumes element
     103             :                                                                     // currently acquired (e.g. not null index and, if applicable,
     104             :                                                                     // not sentinel index).  Element not acquired on return.
     105             : 
     106             :      myele_t * mypool_ele_acquire( myele_t * join                ); // Acquire an element from pool, assumes at least 1 free
     107             :                                                                     // Returns a pointer to element in caller's address space, not
     108             :                                                                     // NULL and, if applicable, not sentinel.  Lifetime is lesser
     109             :                                                                     // of join lifetime and acquired element is released.  Will not
     110             :                                                                     // return a currently acquired element
     111             :      void      mypool_ele_release( myele_t * join, myele_t * ele ); // Release an element to pool by local pointer, assumes element
     112             :                                                                     // currently acquired (e.g. not NULL and, if applicable, not
     113             :                                                                     // sentinel).  Element not acquired on return.
     114             : 
     115             :      You can do this as often as you like in a compilation unit to get
     116             :      different types of pools.  Since it is all static inline, it is
     117             :      fine to do this in a header too.  Additional options to fine tune
     118             :      this are detailed below. */
     119             : 
     120             : #include "../bits/fd_bits.h"
     121             : 
     122             : #ifndef POOL_NAME
     123             : #error "Define POOL_NAME"
     124             : #endif
     125             : 
     126             : /* A POOL_T should be something reasonable to shallow copy
     127             :    with the fields described above. */
     128             : 
     129             : #ifndef POOL_T
     130             : #error "Define POOL_T"
     131             : #endif
     132             : 
     133             : /* POOL_NEXT is the name of the field the pool will clobber for
     134             :    elements currently not allocated. */
     135             : 
     136             : #ifndef POOL_NEXT
     137   159111555 : #define POOL_NEXT next
     138             : #endif
     139             : 
     140             : /* POOL_IDX_T is the type of the POOL_NEXT field.  Should be an unsigned
     141             :    integer type.  The maximum value this type can have is also the
     142             :    maximum number of elements that can be in a pool. */
     143             : 
     144             : #ifndef POOL_IDX_T
     145             : #define POOL_IDX_T ulong
     146             : #endif
     147             : 
     148             : /* If POOL_SENTINEL is non-zero, the pool will reserve element idx
     149             :    0 as a sentinel element (will be considered as always allocated).
     150             :    Setting this also implies that the max for a pool should be at least
     151             :    1. */
     152             : 
     153             : #ifndef POOL_SENTINEL
     154             : #define POOL_SENTINEL 0
     155             : #endif
     156             : 
     157             : /* 0 - local use only
     158             :    1 - library header declaration
     159             :    2 - library implementation */
     160             : 
     161             : #ifndef POOL_IMPL_STYLE
     162             : #define POOL_IMPL_STYLE 0
     163             : #endif
     164             : 
     165             : /* POOL_LAZY enables lazy initialization for faster startup if defined
     166             :    to non-zero.  Decreases pool_reset cost from O(ele_max) to O(1), at
     167             :    the cost of more complex allocation logic (bump allocation). */
     168             : 
     169             : #ifndef POOL_LAZY
     170             : #define POOL_LAZY 0
     171             : #endif
     172             : 
     173             : /* POOL_MAGIC is the magic number that should be used to identify
     174             :    pools of this type in shared memory.  Should be non-zero. */
     175             : 
     176             : #ifndef POOL_MAGIC
     177       12858 : #define POOL_MAGIC (0xF17EDA2CE7900100UL) /* Firedancer pool ver 0 */
     178             : #endif
     179             : 
     180             : #if FD_TMPL_USE_HANDHOLDING
     181             : #include "../log/fd_log.h"
     182             : #endif
     183             : 
     184             : /* Implementation *****************************************************/
     185             : 
     186 31247515527 : #define POOL_(n) FD_EXPAND_THEN_CONCAT3(POOL_NAME,_,n)
     187             : 
     188   158897424 : #define POOL_IDX_NULL ((ulong)((POOL_IDX_T)~0UL))
     189             : 
     190             : #if POOL_IMPL_STYLE==0 || POOL_IMPL_STYLE==1 /* need structures and inlines */
     191             : 
     192             : struct POOL_(private) {
     193             : 
     194             :   ulong magic;     /* ==POOL_MAGIC */
     195             :   ulong max;       /* Max elements in pool, in [POOL_SENTINEL,POOL_IDX_NULL] */
     196             :   ulong free;      /* Num elements in pool available, in [0,max] */
     197             :   ulong free_top;  /* Free stack top, POOL_IDX_NULL no elements currently in pool */
     198             :   ulong free_lazy; /* Next free element (bump allocated) */
     199             : 
     200             :   /* max POOL_T elements here, join points to element 0 */
     201             :   /* element 0 will be the sentinel if POOL_SENTINEL is true */
     202             : 
     203             : };
     204             : 
     205             : typedef struct POOL_(private) POOL_(private_t);
     206             : 
     207             : FD_PROTOTYPES_BEGIN
     208             : 
     209             : /* Private APIs *******************************************************/
     210             : 
     211             : /* pool_private_meta_footprint returns the number of bytes used by a
     212             :    pool metadata region */
     213             : 
     214             : FD_FN_CONST static inline ulong
     215 12935625711 : POOL_(private_meta_footprint)( void ) {
     216 12935625711 :   return fd_ulong_align_up( sizeof(POOL_(private_t)), fd_ulong_max( alignof(POOL_T), 128UL )  );
     217 12935625711 : }
     218             : 
     219             : /* pool_private_meta returns a pointer in the caller's address space to
     220             :    a pool metadata region.  pool_private_meta_const is a const correct
     221             :    version. */
     222             : 
     223             : FD_FN_CONST static inline POOL_(private_t) *
     224  3022515492 : POOL_(private_meta)( POOL_T * join ) {
     225  3022515492 :   return (POOL_(private_t) *)(((ulong)join) - POOL_(private_meta_footprint)());
     226  3022515492 : }
     227             : 
     228             : FD_FN_CONST static inline POOL_(private_t) const *
     229  9913016172 : POOL_(private_meta_const)( POOL_T const * join ) {
     230  9913016172 :   return (POOL_(private_t) const *)(((ulong)join) - POOL_(private_meta_footprint)());
     231  9913016172 : }
     232             : 
     233             : /* Public APIS ********************************************************/
     234             : 
     235             : FD_FN_CONST static inline ulong
     236          12 : POOL_(max_for_footprint)( ulong footprint ) {
     237          12 :   ulong meta_footprint = POOL_(private_meta_footprint)();
     238          12 :   if( FD_UNLIKELY( footprint <= meta_footprint ) ) return 0UL;
     239          12 :   return fd_ulong_min( (footprint - meta_footprint) / sizeof(POOL_T), POOL_IDX_NULL );
     240          12 : }
     241             : 
     242             : FD_FN_CONST static inline ulong
     243      153522 : POOL_(align)( void ) {
     244      153522 :   return fd_ulong_max( alignof(POOL_T), 128UL );
     245      153522 : }
     246             : 
     247             : FD_FN_CONST static inline ulong
     248       58239 : POOL_(footprint)( ulong max ) {
     249             : # if POOL_SENTINEL
     250         120 :   if( FD_UNLIKELY( !max ) ) return 0UL;
     251         114 : # endif
     252       58233 :   ulong align          = POOL_(align)();
     253       58233 :   ulong meta_footprint = POOL_(private_meta_footprint)(); /* Multiple of align */
     254         114 :   ulong data_footprint = fd_ulong_align_up( sizeof(POOL_T)*max, align );
     255       58233 :   ulong thresh         = fd_ulong_min( (ULONG_MAX - align - meta_footprint + 1UL) / sizeof(POOL_T), POOL_IDX_NULL );
     256         114 :   return fd_ulong_if( max > thresh, 0UL, meta_footprint + data_footprint );
     257       58239 : }
     258             : 
     259             : FD_PROTOTYPES_END
     260             : 
     261             : #endif
     262             : 
     263             : FD_PROTOTYPES_BEGIN
     264             : 
     265             : #if POOL_IMPL_STYLE==1 /* need prototypes */
     266             : 
     267             : FD_FN_UNUSED void * /* Work around -Winline */
     268             : POOL_(new)( void * shmem,
     269             :             ulong  max );
     270             : 
     271             : FD_FN_UNUSED POOL_T *
     272             : POOL_(join)( void * shpool );
     273             : 
     274             : FD_FN_UNUSED void
     275             : POOL_(reset)( POOL_T * join );
     276             : 
     277             : #else /* need implementations */
     278             : 
     279             : #if POOL_IMPL_STYLE==0 /* local only */
     280             : #define POOL_IMPL_STATIC FD_FN_UNUSED static
     281             : #else
     282             : #define POOL_IMPL_STATIC
     283             : #endif
     284             : 
     285             : POOL_IMPL_STATIC void * /* Work around -Winline */
     286             : POOL_(new)( void * shmem,
     287       12879 :             ulong  max ) {
     288             : 
     289       12879 :   if( FD_UNLIKELY( !shmem )                                               ) return NULL;
     290       12876 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, POOL_(align)() ) ) ) return NULL;
     291       12873 :   if( FD_UNLIKELY( !POOL_(footprint)( max ) )                             ) return NULL;
     292             : 
     293       12864 :   POOL_T *           join = (POOL_T *)(((ulong)shmem) + POOL_(private_meta_footprint)());
     294       12864 :   POOL_(private_t) * meta = POOL_(private_meta)( join );
     295             : 
     296       12864 :   meta->max  = max;
     297       12864 :   meta->free = max;
     298             : 
     299             : # if POOL_LAZY
     300        1908 :   meta->free_top = POOL_IDX_NULL;
     301        1908 :   if( FD_UNLIKELY( !max ) ) {
     302           0 :     meta->free_lazy = POOL_IDX_NULL; /* Not reachable if POOL_SENTINEL set (footprint test above fails) */
     303        1908 :   } else {
     304        1908 :     meta->free_lazy = 0UL;
     305             : #   if POOL_SENTINEL
     306             :     meta->free_lazy = 1UL;
     307             :     meta->free--;
     308             : #   endif
     309        1908 :   }
     310             : # else
     311       10956 :   if( FD_UNLIKELY( !max ) ) meta->free_top = POOL_IDX_NULL; /* Not reachable if POOL_SENTINEL set (footprint test above fails) */
     312       10947 :   else {
     313       10947 :     meta->free_top = 0UL;
     314   286447488 :     for( ulong idx=1UL; idx<max; idx++ ) join[ idx-1UL ].POOL_NEXT = (POOL_IDX_T)idx;
     315       10947 :     join[ max-1UL ].POOL_NEXT = (POOL_IDX_T)POOL_IDX_NULL;
     316             : 
     317             : #   if POOL_SENTINEL
     318             :     meta->free_top = 1UL;
     319             :     meta->free--;
     320          27 :     join[ 0 ].POOL_NEXT = (POOL_IDX_T)POOL_IDX_NULL;
     321             : #   endif
     322       10947 :   }
     323             : # endif
     324             : 
     325       12864 :   FD_COMPILER_MFENCE();
     326       12864 :   FD_VOLATILE( meta->magic ) = (POOL_MAGIC);
     327       12864 :   FD_COMPILER_MFENCE();
     328             : 
     329       12864 :   return shmem;
     330       12873 : }
     331             : 
     332             : POOL_IMPL_STATIC void
     333        4194 : POOL_(reset)( POOL_T * join ) {
     334        4194 :   POOL_(private_t) * meta = POOL_(private_meta)( join );
     335             : 
     336        4194 :   meta->free = meta->max;
     337             : 
     338             : # if POOL_LAZY
     339        2484 :   meta->free_top = POOL_IDX_NULL;
     340        2484 :   if( FD_UNLIKELY( !meta->max ) ) {
     341           0 :     meta->free_lazy = POOL_IDX_NULL; /* Not reachable if POOL_SENTINEL set (footprint test above fails) */
     342        2484 :   } else {
     343        2484 :     meta->free_lazy = 0UL;
     344             : #   if POOL_SENTINEL
     345             :     meta->free_lazy = 1UL;
     346             :     meta->free--;
     347             : #   endif
     348        2484 :   }
     349             : # else
     350        1710 :   if( FD_UNLIKELY( !meta->max ) ) meta->free_top = POOL_IDX_NULL; /* Not reachable if POOL_SENTINEL set (footprint test above fails) */
     351        1710 :   else {
     352        1710 :     meta->free_top = 0UL;
     353      236292 :     for( ulong idx=1UL; idx<meta->max; idx++ ) join[ idx-1UL ].POOL_NEXT = (POOL_IDX_T)idx;
     354        1710 :     join[ meta->max-1UL ].POOL_NEXT = (POOL_IDX_T)POOL_IDX_NULL;
     355             : 
     356             : #   if POOL_SENTINEL
     357             :     meta->free_top = 1UL;
     358             :     meta->free--;
     359             :     join[ 0 ].POOL_NEXT = (POOL_IDX_T)POOL_IDX_NULL;
     360             : #   endif
     361        1710 :   }
     362             : # endif
     363        4194 : }
     364             : 
     365             : POOL_IMPL_STATIC POOL_T *
     366       14874 : POOL_(join)( void * shpool ) {
     367       14874 :   if( FD_UNLIKELY( !shpool                                               ) ) return NULL;
     368       14871 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shpool, POOL_(align)() ) ) ) return NULL;
     369             : 
     370       14868 :   POOL_T *           join = (POOL_T *)(((ulong)shpool) + POOL_(private_meta_footprint)());
     371       14868 :   POOL_(private_t) * meta = POOL_(private_meta)( join );
     372             : 
     373       14868 :   if( FD_UNLIKELY( FD_VOLATILE_CONST( meta->magic )!=(POOL_MAGIC) ) ) return NULL;
     374             : 
     375       14865 :   return join;
     376       14868 : }
     377             : 
     378             : #endif
     379             : 
     380             : FD_PROTOTYPES_END
     381             : 
     382             : FD_PROTOTYPES_BEGIN
     383             : 
     384             : #if POOL_IMPL_STYLE==0 || POOL_IMPL_STYLE==1 /* need structures and inlines */
     385             : 
     386             : FD_FN_CONST static inline void *
     387        4389 : POOL_(leave)( POOL_T * join ) {
     388        4389 :   if( FD_UNLIKELY( !join ) ) return NULL;
     389        4386 :   return (void *)(((ulong)join) - POOL_(private_meta_footprint)());
     390        4389 : }
     391             : 
     392             : static inline void *
     393        3690 : POOL_(delete)( void * shpool ) {
     394        3690 :   if( FD_UNLIKELY( !shpool ) ) return NULL;
     395        3687 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shpool, POOL_(align)() ) ) ) return NULL;
     396             : 
     397        3684 :   POOL_T *           join = (POOL_T *)(((ulong)shpool) + POOL_(private_meta_footprint)());
     398        3684 :   POOL_(private_t) * meta = POOL_(private_meta)( join );
     399             : 
     400        3684 :   if( FD_UNLIKELY( FD_VOLATILE_CONST( meta->magic )!=(POOL_MAGIC) ) ) return NULL;
     401             : 
     402        3681 :   FD_COMPILER_MFENCE();
     403        3681 :   FD_VOLATILE( meta->magic ) = 0UL;
     404        3681 :   FD_COMPILER_MFENCE();
     405             : 
     406        3681 :   return shpool;
     407        3684 : }
     408             : 
     409             : /* Special values */
     410             : 
     411       21822 : FD_FN_CONST static inline ulong          POOL_(idx_null)          ( POOL_T const * join ) { (void)join; return POOL_IDX_NULL; }
     412           3 : FD_FN_CONST static inline POOL_T *       POOL_(ele_null)          ( POOL_T       * join ) { (void)join; return NULL;          }
     413           3 : FD_FN_CONST static inline POOL_T const * POOL_(ele_null_const)    ( POOL_T const * join ) { (void)join; return NULL;          }
     414             : 
     415             : #if POOL_SENTINEL
     416           3 : FD_FN_CONST static inline ulong          POOL_(idx_sentinel)      ( POOL_T const * join ) { (void)join; return 0UL;  }
     417           9 : FD_FN_CONST static inline POOL_T *       POOL_(ele_sentinel)      ( POOL_T *       join ) { return join; }
     418           3 : FD_FN_CONST static inline POOL_T const * POOL_(ele_sentinel_const)( POOL_T const * join ) { return join; }
     419             : #endif
     420             : 
     421             : /* Address space conversion */
     422             : 
     423             : FD_FN_PURE static inline int
     424             : POOL_(idx_test)( POOL_T const * join,
     425   152328195 :                  ulong          idx ) {
     426   152328195 :   ulong max = POOL_(private_meta_const)( join )->max;
     427   152328195 :   return (idx<max) | (idx==POOL_IDX_NULL);
     428   152328195 : }
     429             : 
     430             : FD_FN_PURE static inline int
     431             : POOL_(ele_test)( POOL_T const * join,
     432  7046803065 :                  POOL_T const * ele ) {
     433  7046803065 :   ulong max = POOL_(private_meta_const)( join )->max;
     434  7046803065 :   ulong idx = (ulong)(ele - join);
     435  7046803065 :   FD_COMPILER_FORGET( idx ); /* prevent compiler from optimizing out alignment test */
     436  7046803065 :   return (!ele) | ((idx<max) & ((ulong)ele==((ulong)join+(idx*sizeof(POOL_T))))); /* last test checks alignment */
     437  7046803065 : }
     438             : 
     439             : FD_FN_CONST static inline ulong
     440             : POOL_(idx)( POOL_T const * join,
     441   301693197 :             POOL_T const * ele ) {
     442             : # if FD_TMPL_USE_HANDHOLDING
     443             :   if( FD_UNLIKELY( !POOL_(ele_test)( join, ele ) ) ) FD_LOG_CRIT(( "no such element" ));
     444             : # endif
     445   301693197 :   return ele ? (ulong)(ele-join) : POOL_IDX_NULL;
     446   301693197 : }
     447             : 
     448             : FD_FN_CONST static inline POOL_T *
     449             : POOL_(ele)( POOL_T *   join,
     450     3461475 :             ulong      idx ) {
     451             : # if FD_TMPL_USE_HANDHOLDING
     452             :   if( FD_UNLIKELY( !POOL_(idx_test)( join, idx ) ) ) FD_LOG_CRIT(( "no such index" ));
     453             : # endif
     454     3461475 :   return (idx==POOL_IDX_NULL) ? NULL : (join + idx);
     455     3461475 : }
     456             : 
     457             : FD_FN_CONST static inline POOL_T const *
     458             : POOL_(ele_const)( POOL_T const *   join,
     459     2994693 :                   ulong            idx ) {
     460             : # if FD_TMPL_USE_HANDHOLDING
     461             :   if( FD_UNLIKELY( !POOL_(idx_test)( join, idx ) ) ) FD_LOG_CRIT(( "no such index" ));
     462             : # endif
     463     2994693 :   return (idx==POOL_IDX_NULL) ? NULL : (join + idx);
     464     2994693 : }
     465             : 
     466             : /* Accessors */
     467             : 
     468     3964653 : FD_FN_PURE static inline ulong POOL_(max) ( POOL_T const * join ) { return POOL_(private_meta_const)( join )->max;  }
     469  1191129165 : FD_FN_PURE static inline ulong POOL_(free)( POOL_T const * join ) { return POOL_(private_meta_const)( join )->free; }
     470             : 
     471             : FD_FN_PURE static inline ulong
     472  1518791094 : POOL_(used)( POOL_T const * join ) {
     473  1518791094 :   POOL_(private_t) const * meta = POOL_(private_meta_const)( join );
     474  1518791094 :   return meta->max - meta->free;
     475  1518791094 : }
     476             : 
     477             : /* Operations */
     478             : 
     479             : static inline ulong
     480  1511803689 : POOL_(idx_acquire)( POOL_T * join ) {
     481  1511803689 :   POOL_(private_t) * meta = POOL_(private_meta)( join );
     482             : # if FD_TMPL_USE_HANDHOLDING
     483             :   if( FD_UNLIKELY( !meta->free ) ) FD_LOG_CRIT(( "pool is full" ));
     484             : # endif
     485  1511803689 :   ulong idx = meta->free_top;
     486             : # if POOL_LAZY
     487   149342667 :   if( FD_UNLIKELY( idx==POOL_IDX_NULL ) ) {
     488        2616 :     idx = meta->free_lazy;
     489        2616 :     ulong nxt = idx + 1UL;
     490        2616 :     meta->free_lazy = ( nxt<meta->max ) ? nxt : POOL_IDX_NULL;
     491   149340051 :   } else {
     492   149340051 :     meta->free_top = (ulong)join[ idx ].POOL_NEXT;
     493   149340051 :   }
     494             : # else
     495  1362461022 :   meta->free_top = (ulong)join[ idx ].POOL_NEXT;
     496             : # endif
     497  1511803689 :   meta->free--;
     498  1511803689 :   return idx;
     499  1511803689 : }
     500             : 
     501             : static inline void
     502             : POOL_(idx_release)( POOL_T * join,
     503  1510676193 :                     ulong    idx ) {
     504  1510676193 :   POOL_(private_t) * meta = POOL_(private_meta)( join );
     505             : # if FD_TMPL_USE_HANDHOLDING
     506             :   if( FD_UNLIKELY( (meta->max<=idx) | (idx==POOL_IDX_NULL) ) ) FD_LOG_CRIT(( "invalid index" ));
     507             : # if POOL_SENTINEL
     508             :   if( FD_UNLIKELY( POOL_(idx_sentinel)( join )==idx ) ) FD_LOG_CRIT(( "cannot releaes sentinel" ));
     509             :   if( FD_UNLIKELY( meta->free>=meta->max-1 ) ) FD_LOG_CRIT(( "pool is empty" ));
     510             : # else
     511             :   if( FD_UNLIKELY( meta->free>=meta->max ) ) FD_LOG_CRIT(( "pool is empty" ));
     512             : # endif
     513             : # endif
     514  1510676193 :   join[ idx ].POOL_NEXT = (POOL_IDX_T)meta->free_top;
     515  1510676193 :   meta->free_top = idx;
     516  1510676193 :   meta->free++;
     517  1510676193 : }
     518             : 
     519   424791030 : static inline POOL_T * POOL_(ele_acquire)( POOL_T * join               ) { return join + POOL_(idx_acquire)( join ); }
     520   410202303 : static inline void     POOL_(ele_release)( POOL_T * join, POOL_T * ele ) { POOL_(idx_release)( join, (ulong)(ele - join) );   }
     521             : 
     522             : /* TODO: consider zeroing out pool mem on new? */
     523             : 
     524             : /* TODO: consider providing element size and alignment as metadata? */
     525             : 
     526             : /* TODO: consider lockfree concurrent version with ABA tagged free_top? */
     527             : 
     528             : /* TODO: consider a verify and rebuild that work via most sig bit of
     529             :    POOL_NEXT for element marking (the POOL_NEXT field in the structure
     530             :    would have to become dedicated to the pool though). */
     531             : 
     532             : #endif
     533             : 
     534             : FD_PROTOTYPES_END
     535             : 
     536             : #undef POOL_IMPL_STATIC
     537             : #undef POOL_IDX_NULL
     538             : #undef POOL_
     539             : 
     540             : #undef POOL_MAGIC
     541             : #undef POOL_LAZY
     542             : #undef POOL_SENTINEL
     543             : #undef POOL_IDX_T
     544             : #undef POOL_NEXT
     545             : #undef POOL_IMPL_STYLE
     546             : #undef POOL_T
     547             : #undef POOL_NAME

Generated by: LCOV version 1.14