LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 384 524 73.3 %
Date: 2025-08-05 05:04:49 Functions: 42 234 17.9 %

          Line data    Source code
       1             : #include "fd_bank.h"
       2             : #include "sysvar/fd_sysvar_epoch_schedule.h"
       3             : 
       4             : ulong
       5          24 : fd_bank_align( void ) {
       6          24 :   return alignof(fd_bank_t);
       7          24 : }
       8             : 
       9             : ulong
      10           6 : fd_bank_footprint( void ) {
      11           6 :   ulong l = FD_LAYOUT_INIT;
      12           6 :   l = FD_LAYOUT_APPEND( l, fd_bank_align(), sizeof(fd_bank_t) );
      13           6 :   return FD_LAYOUT_FINI( l, fd_bank_align() );
      14           6 : }
      15             : 
      16             : /* Bank accesssors */
      17             : 
      18             : #define HAS_COW_1(type, name, footprint, align, has_lock)                                                          \
      19             :   type const *                                                                                                     \
      20          27 :   fd_bank_##name##_locking_query( fd_bank_t * bank ) {                                                             \
      21          27 :     fd_rwlock_read( &bank->name##_lock );                                                                          \
      22          27 :     /* If the pool element hasn't been setup yet, then return NULL */                                              \
      23          27 :     fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank );                                          \
      24          27 :     if( FD_UNLIKELY( name##_pool==NULL ) ) {                                                                       \
      25           0 :       FD_LOG_CRIT(( "NULL " #name " pool" ));                                                                      \
      26           0 :     }                                                                                                              \
      27          27 :     if( bank->name##_pool_idx==fd_bank_##name##_pool_idx_null( name##_pool ) ) {                                   \
      28           6 :       return NULL;                                                                                                 \
      29           6 :     }                                                                                                              \
      30          27 :     fd_bank_##name##_t * bank_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx );            \
      31          21 :     return (type *)bank_##name->data;                                                                              \
      32          27 :   }                                                                                                                \
      33             :   void                                                                                                             \
      34          27 :   fd_bank_##name##_end_locking_query( fd_bank_t * bank ) {                                                         \
      35          27 :     fd_rwlock_unread( &bank->name##_lock );                                                                        \
      36          27 :   }                                                                                                                \
      37             :   type *                                                                                                           \
      38          21 :   fd_bank_##name##_locking_modify( fd_bank_t * bank ) {                                                            \
      39          21 :     fd_rwlock_write( &bank->name##_lock );                                                                         \
      40          21 :     /* If the dirty flag is set, then we already have a pool element */                                            \
      41          21 :     /* that was copied over for the current bank. We can simply just */                                            \
      42          21 :     /* query the pool element and return it. */                                                                    \
      43          21 :     fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank );                                          \
      44          21 :     if( FD_UNLIKELY( name##_pool==NULL ) ) {                                                                       \
      45           0 :       FD_LOG_CRIT(( "NULL " #name " pool" ));                                                                      \
      46           0 :     }                                                                                                              \
      47          21 :     if( bank->name##_dirty ) {                                                                                     \
      48           0 :       fd_bank_##name##_t * bank_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx );          \
      49           0 :       return (type *)bank_##name->data;                                                                            \
      50           0 :     }                                                                                                              \
      51          21 :     if( FD_UNLIKELY( !fd_bank_##name##_pool_free( name##_pool ) ) ) {                                              \
      52           0 :       FD_LOG_CRIT(( "Failed to acquire " #name " pool element: pool is full" ));                                   \
      53           0 :     }                                                                                                              \
      54          21 :     fd_bank_##name##_t * child_##name = fd_bank_##name##_pool_ele_acquire( name##_pool );                          \
      55          21 :     if( FD_UNLIKELY( !child_##name ) ) {                                                                           \
      56           0 :       FD_LOG_CRIT(( "Failed to acquire " #name " pool element" ));                                                 \
      57           0 :     }                                                                                                              \
      58          21 :     /* If the dirty flag has not been set yet, we need to allocated a */                                           \
      59          21 :     /* new pool element and copy over the data from the parent idx.   */                                           \
      60          21 :     /* We also need to mark the dirty flag. */                                                                     \
      61          21 :     ulong child_idx = fd_bank_##name##_pool_idx( name##_pool, child_##name );                                      \
      62          21 :     if( bank->name##_pool_idx!=fd_bank_##name##_pool_idx_null( name##_pool ) ) {                                   \
      63           6 :       fd_bank_##name##_t * parent_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx );        \
      64           6 :       fd_memcpy( child_##name->data, parent_##name->data, fd_bank_##name##_footprint );                            \
      65           6 :     }                                                                                                              \
      66          21 :     bank->name##_pool_idx = child_idx;                                                                             \
      67          21 :     bank->name##_dirty    = 1;                                                                                     \
      68          21 :     return (type *)child_##name->data;                                                                             \
      69          21 :   }                                                                                                                \
      70             :   void                                                                                                             \
      71          21 :   fd_bank_##name##_end_locking_modify( fd_bank_t * bank ) {                                                        \
      72          21 :     fd_rwlock_unwrite( &bank->name##_lock );                                                                       \
      73          21 :   }
      74             : 
      75             : 
      76             : #define HAS_LOCK_0(type, name) \
      77             :   type const *                                             \
      78        1470 :   fd_bank_##name##_query( fd_bank_t const * bank ) {       \
      79        1470 :     return (type const *)fd_type_pun_const( bank->name );  \
      80        1470 :   }                                                        \
      81             :   type *                                                   \
      82         912 :   fd_bank_##name##_modify( fd_bank_t * bank ) {            \
      83         912 :     return (type *)fd_type_pun( bank->name );              \
      84         912 :   }
      85             : 
      86             : #define HAS_LOCK_1(type, name)                              \
      87             :   type const *                                              \
      88             :   fd_bank_##name##_locking_query( fd_bank_t * bank ) {      \
      89             :     fd_rwlock_read( &bank->name##_lock );                   \
      90             :     return (type const *)fd_type_pun_const( bank->name );   \
      91             :   }                                                         \
      92             :   type *                                                    \
      93             :   fd_bank_##name##_locking_modify( fd_bank_t * bank ) {     \
      94             :     fd_rwlock_write( &bank->name##_lock );                  \
      95             :     return (type *)fd_type_pun( bank->name );               \
      96             :   }                                                         \
      97             :   void                                                      \
      98             :   fd_bank_##name##_end_locking_query( fd_bank_t * bank ) {  \
      99             :     fd_rwlock_unread( &bank->name##_lock );                 \
     100             :   }                                                         \
     101             :   void                                                      \
     102             :   fd_bank_##name##_end_locking_modify( fd_bank_t * bank ) { \
     103             :     fd_rwlock_unwrite( &bank->name##_lock );                \
     104             :   }
     105             : 
     106             : #define HAS_COW_0(type, name, footprint, align, has_lock)   \
     107             :   HAS_LOCK_##has_lock(type, name)                           \
     108             :   void                                                      \
     109         945 :   fd_bank_##name##_set( fd_bank_t * bank, type value ) {    \
     110         945 :     FD_STORE( type, bank->name, value );                    \
     111         945 :   }                                                         \
     112             :   type                                                      \
     113         519 :   fd_bank_##name##_get( fd_bank_t const * bank ) {          \
     114         519 :     type val = FD_LOAD( type, bank->name );                 \
     115         519 :     return val;                                             \
     116         519 :   }
     117             : 
     118             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     119             :   HAS_COW_##cow(type, name, footprint, align, has_lock)
     120           6 : FD_BANKS_ITER(X)
     121           6 : #undef X
     122           6 : #undef HAS_COW_0
     123           6 : #undef HAS_COW_1
     124           6 : #undef HAS_LOCK_0
     125           6 : #undef HAS_LOCK_1
     126           6 : 
     127           6 : /**********************************************************************/
     128           6 : 
     129           6 : ulong
     130         135 : fd_banks_align( void ) {
     131             :   /* TODO: The magic number here can probably be removed. */
     132         135 :   return 128UL;
     133         135 : }
     134             : 
     135             : ulong
     136          12 : fd_banks_footprint( ulong max_total_banks, ulong FD_PARAM_UNUSED max_fork_width ) {
     137             : 
     138             :   /* max_fork_width is used in the macro below. */
     139             : 
     140          12 :   ulong map_chain_cnt = fd_ulong_pow2_up( max_total_banks );
     141             : 
     142          12 :   ulong l = FD_LAYOUT_INIT;
     143          12 :   l = FD_LAYOUT_APPEND( l, fd_banks_align(),      sizeof(fd_banks_t) );
     144          12 :   l = FD_LAYOUT_APPEND( l, fd_banks_pool_align(), fd_banks_pool_footprint( max_total_banks ) );
     145          12 :   l = FD_LAYOUT_APPEND( l, fd_banks_map_align(),  fd_banks_map_footprint( map_chain_cnt ) );
     146             : 
     147             :   /* Need to count the footprint for all of the CoW pools. The footprint
     148             :      on each CoW pool depends on if the field limits the fork width. */
     149             : 
     150          12 :   #define HAS_COW_1_LIMIT_1(name) \
     151          24 :     l = FD_LAYOUT_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_fork_width ) );
     152             : 
     153          12 :   #define HAS_COW_1_LIMIT_0(name) \
     154          72 :     l = FD_LAYOUT_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_total_banks ) );
     155             : 
     156             :   /* Do nothing for these. */
     157          12 :   #define HAS_COW_0_LIMIT_0(name)
     158             : 
     159          12 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock)  \
     160          96 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     161          96 :   FD_BANKS_ITER(X)
     162          12 :   #undef X
     163          12 :   #undef HAS_COW_0_LIMIT_0
     164          12 :   #undef HAS_COW_1_LIMIT_0
     165          12 :   #undef HAS_COW_1_LIMIT_1
     166             : 
     167          12 :   return FD_LAYOUT_FINI( l, fd_banks_align() );
     168          12 : }
     169             : 
     170             : void *
     171           6 : fd_banks_new( void * shmem, ulong max_total_banks, ulong max_fork_width ) {
     172             : 
     173           6 :   fd_banks_t * banks = (fd_banks_t *)shmem;
     174             : 
     175           6 :   if( FD_UNLIKELY( !banks ) ) {
     176           0 :     FD_LOG_WARNING(( "NULL banks" ));
     177           0 :     return NULL;
     178           0 :   }
     179             : 
     180           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks, fd_banks_align() ) ) ) {
     181           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     182           0 :     return NULL;
     183           0 :   }
     184             : 
     185             :   /* Set the rwlock to unlocked. */
     186           6 :   fd_rwlock_unwrite( &banks->rwlock );
     187             : 
     188           6 :   ulong map_chain_cnt = fd_ulong_pow2_up( max_total_banks );
     189             : 
     190             :   /* First, layout the banks and the pool/map used by fd_banks_t. */
     191           6 :   FD_SCRATCH_ALLOC_INIT( l, banks );
     192           6 :   banks           = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),      sizeof(fd_banks_t) );
     193           6 :   void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(), fd_banks_pool_footprint( max_total_banks ) );
     194           6 :   void * map_mem  = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_map_align(),  fd_banks_map_footprint( map_chain_cnt ) );
     195             : 
     196             :   /* Need to layout all of the CoW pools. */
     197           0 :   #define HAS_COW_1_LIMIT_1(name) \
     198          12 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_fork_width ) );
     199             : 
     200           0 :   #define HAS_COW_1_LIMIT_0(name) \
     201          36 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_total_banks ) );
     202             : 
     203             :   /* Do nothing for these. */
     204           0 :   #define HAS_COW_0_LIMIT_0(name)
     205             : 
     206           0 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     207          48 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     208          48 :   FD_BANKS_ITER(X)
     209           0 :   #undef X
     210           0 :   #undef HAS_COW_0_LIMIT_0
     211           0 :   #undef HAS_COW_1_LIMIT_0
     212           0 :   #undef HAS_COW_1_LIMIT_1
     213             : 
     214           6 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() ) != (ulong)banks + fd_banks_footprint( max_total_banks, max_fork_width ) ) ) {
     215           0 :     FD_LOG_WARNING(( "fd_banks_new: bad layout" ));
     216           0 :     return NULL;
     217           0 :   }
     218             : 
     219           6 :   void * pool = fd_banks_pool_new( pool_mem, max_total_banks );
     220           6 :   if( FD_UNLIKELY( !pool ) ) {
     221           0 :     FD_LOG_WARNING(( "Failed to create bank pool" ));
     222           0 :     return NULL;
     223           0 :   }
     224             : 
     225           6 :   fd_bank_t * bank_pool = fd_banks_pool_join( pool );
     226           6 :   if( FD_UNLIKELY( !bank_pool ) ) {
     227           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     228           0 :     return NULL;
     229           0 :   }
     230             : 
     231           6 :   fd_banks_set_bank_pool( banks, bank_pool );
     232             : 
     233           6 :   void * map = fd_banks_map_new( map_mem, map_chain_cnt, 999UL );
     234           6 :   if( FD_UNLIKELY( !map ) ) {
     235           0 :     FD_LOG_WARNING(( "Failed to create bank map" ));
     236           0 :     return NULL;
     237           0 :   }
     238             : 
     239           6 :   fd_banks_map_t * bank_map = fd_banks_map_join( map_mem );
     240           6 :   if( FD_UNLIKELY( !bank_map ) ) {
     241           0 :     FD_LOG_WARNING(( "Failed to join bank map" ));
     242           0 :     return NULL;
     243           0 :   }
     244             : 
     245           6 :   fd_banks_set_bank_map( banks, bank_map );
     246             : 
     247             :   /* Now, call _new() and _join() for all of the CoW pools. */
     248           6 :   #define HAS_COW_1_LIMIT_1(name)                                                     \
     249          12 :     void * name##_mem = fd_bank_##name##_pool_new( name##_pool_mem, max_fork_width ); \
     250          12 :     if( FD_UNLIKELY( !name##_mem ) ) {                                                \
     251           0 :       FD_LOG_WARNING(( "Failed to create " #name " pool" ));                          \
     252           0 :       return NULL;                                                                    \
     253           0 :     }                                                                                 \
     254          12 :     fd_bank_##name##_t * name##_pool = fd_bank_##name##_pool_join( name##_pool_mem ); \
     255          12 :     if( FD_UNLIKELY( !name##_pool ) ) {                                               \
     256           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                            \
     257           0 :       return NULL;                                                                    \
     258           0 :     }                                                                                 \
     259          12 :     fd_banks_set_##name##_pool( banks, name##_pool );
     260             : 
     261           6 :   #define HAS_COW_1_LIMIT_0(name)                                                      \
     262          36 :     void * name##_mem = fd_bank_##name##_pool_new( name##_pool_mem, max_total_banks ); \
     263          36 :     if( FD_UNLIKELY( !name##_mem ) ) {                                                 \
     264           0 :       FD_LOG_WARNING(( "Failed to create " #name " pool" ));                           \
     265           0 :       return NULL;                                                                     \
     266           0 :     }                                                                                  \
     267          36 :     fd_bank_##name##_t * name##_pool = fd_bank_##name##_pool_join( name##_pool_mem );  \
     268          36 :     if( FD_UNLIKELY( !name##_pool ) ) {                                                \
     269           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                             \
     270           0 :       return NULL;                                                                     \
     271           0 :     }                                                                                  \
     272          36 :     fd_banks_set_##name##_pool( banks, name##_pool );
     273             : 
     274             :   /* Do nothing for these. */
     275           6 :   #define HAS_COW_0_LIMIT_0(name)
     276             : 
     277           6 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     278          48 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     279          48 :   FD_BANKS_ITER(X)
     280           6 :   #undef X
     281           6 :   #undef HAS_COW_0_LIMIT_0
     282           6 :   #undef HAS_COW_1_LIMIT_1
     283           6 :   #undef HAS_COW_1_LIMIT_0
     284             : 
     285           6 :   banks->max_total_banks = max_total_banks;
     286           6 :   banks->max_fork_width  = max_fork_width;
     287           6 :   banks->magic           = FD_BANKS_MAGIC;
     288             : 
     289           6 :   return shmem;
     290          90 : }
     291             : 
     292             : fd_banks_t *
     293          12 : fd_banks_join( void * mem ) {
     294          12 :   fd_banks_t * banks = (fd_banks_t *)mem;
     295             : 
     296          12 :   if( FD_UNLIKELY( !banks ) ) {
     297           0 :     FD_LOG_WARNING(( "NULL banks" ));
     298           0 :     return NULL;
     299           0 :   }
     300             : 
     301          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks, fd_banks_align() ) ) ) {
     302           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     303           0 :     return NULL;
     304           0 :   }
     305             : 
     306          12 :   if( FD_UNLIKELY( banks->magic!=FD_BANKS_MAGIC ) ) {
     307           3 :     FD_LOG_WARNING(( "Invalid banks magic" ));
     308           3 :     return NULL;
     309           3 :   }
     310             : 
     311           9 :   ulong map_chain_cnt = fd_ulong_pow2_up( banks->max_total_banks );
     312             : 
     313           9 :   FD_SCRATCH_ALLOC_INIT( l, banks );
     314           9 :   banks           = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),      sizeof(fd_banks_t) );
     315           9 :   void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(), fd_banks_pool_footprint( banks->max_total_banks ) );
     316           9 :   void * map_mem  = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_map_align(),  fd_banks_map_footprint( map_chain_cnt ) );
     317             : 
     318             :   /* Need to layout all of the CoW pools. */
     319           0 :   #define HAS_COW_1_LIMIT_1(name) \
     320          18 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( banks->max_fork_width ) );
     321             : 
     322           0 :   #define HAS_COW_1_LIMIT_0(name) \
     323          54 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( banks->max_total_banks ) );
     324             : 
     325             :   /* Don't need to layout if not CoW. */
     326           0 :   #define HAS_COW_0_LIMIT_0(name)
     327             : 
     328           0 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     329          72 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     330          72 :   FD_BANKS_ITER(X)
     331           0 :   #undef X
     332           0 :   #undef HAS_COW_0_LIMIT_0
     333           0 :   #undef HAS_COW_1_LIMIT_0
     334           0 :   #undef HAS_COW_1_LIMIT_1
     335             : 
     336           9 :   FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() );
     337             : 
     338          72 :   fd_bank_t * banks_pool = fd_banks_get_bank_pool( banks );
     339          72 :   if( FD_UNLIKELY( !banks_pool ) ) {
     340           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     341           0 :     return NULL;
     342           0 :   }
     343             : 
     344           9 :   if( FD_UNLIKELY( banks_pool!=fd_banks_pool_join( pool_mem ) ) ) {
     345           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     346           0 :     return NULL;
     347           0 :   }
     348             : 
     349           9 :   fd_banks_map_t * bank_map = fd_banks_get_bank_map( banks );
     350           9 :   if( FD_UNLIKELY( !bank_map ) ) {
     351           0 :     FD_LOG_WARNING(( "Failed to join bank map" ));
     352           0 :     return NULL;
     353           0 :   }
     354             : 
     355           9 :   if( FD_UNLIKELY( bank_map!=fd_banks_map_join( map_mem ) ) ) {
     356           0 :     FD_LOG_WARNING(( "Failed to join bank map" ));
     357           0 :     return NULL;
     358           0 :   }
     359             : 
     360             :   /* Now, call _join() for all of the CoW pools. */
     361           9 :   #define HAS_COW_1(name)                                                             \
     362          72 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );           \
     363          72 :     if( FD_UNLIKELY( !name##_pool ) ) {                                               \
     364           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                            \
     365           0 :       return NULL;                                                                    \
     366           0 :     }                                                                                 \
     367          72 :     if( FD_UNLIKELY( name##_pool!=fd_bank_##name##_pool_join( name##_pool_mem ) ) ) { \
     368           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                            \
     369           0 :       return NULL;                                                                    \
     370           0 :     }
     371             : 
     372             :   /* Do nothing when the field is not CoW. */
     373           9 :   #define HAS_COW_0(name)
     374             : 
     375           9 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     376          72 :     HAS_COW_##cow(name)
     377         144 :   FD_BANKS_ITER(X)
     378           9 :   #undef X
     379           9 :   #undef HAS_COW_0
     380           9 :   #undef HAS_COW_1
     381             : 
     382             : 
     383           9 :   return banks;
     384         144 : }
     385             : 
     386             : void *
     387           9 : fd_banks_leave( fd_banks_t * banks ) {
     388             : 
     389           9 :   if( FD_UNLIKELY( !banks ) ) {
     390           0 :     FD_LOG_WARNING(( "NULL banks" ));
     391           0 :     return NULL;
     392           0 :   }
     393             : 
     394           9 :   return (void *)banks;
     395           9 : }
     396             : 
     397             : void *
     398           3 : fd_banks_delete( void * shmem ) {
     399             : 
     400           3 :   if( FD_UNLIKELY( !shmem ) ) {
     401           0 :     FD_LOG_WARNING(( "NULL banks" ));
     402           0 :     return NULL;
     403           0 :   }
     404             : 
     405           3 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shmem, fd_banks_align() ) ) ) {
     406           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     407           0 :     return NULL;
     408           0 :   }
     409             : 
     410           3 :   fd_banks_t * banks = (fd_banks_t *)shmem;
     411           3 :   banks->magic = 0UL;
     412             : 
     413           3 :   return shmem;
     414           3 : }
     415             : 
     416             : fd_bank_t *
     417           6 : fd_banks_init_bank( fd_banks_t * banks, ulong slot ) {
     418             : 
     419           6 :   if( FD_UNLIKELY( !banks ) ) {
     420           0 :     FD_LOG_WARNING(( "NULL banks" ));
     421           0 :     return NULL;
     422           0 :   }
     423             : 
     424           6 :   fd_bank_t *      bank_pool = fd_banks_get_bank_pool( banks );
     425           6 :   fd_banks_map_t * bank_map  = fd_banks_get_bank_map( banks );
     426             : 
     427           6 :   fd_bank_t * bank = fd_banks_pool_ele_acquire( bank_pool );
     428           6 :   if( FD_UNLIKELY( bank==NULL ) ) {
     429           0 :     FD_LOG_WARNING(( "Failed to acquire bank" ));
     430           0 :     return NULL;
     431           0 :   }
     432             : 
     433           6 :   memset( bank, 0, fd_bank_footprint() );
     434             : 
     435           6 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
     436           6 :   bank->slot_       = slot;
     437           6 :   bank->next        = null_idx;
     438           6 :   bank->parent_idx  = null_idx;
     439           6 :   bank->child_idx   = null_idx;
     440           6 :   bank->sibling_idx = null_idx;
     441             : 
     442             :   /* Set all CoW fields to null. */
     443           6 :   #define HAS_COW_1(name)                                                             \
     444          48 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );           \
     445          48 :     fd_bank_set_##name##_pool( bank, name##_pool );                                   \
     446          48 :     bank->name##_pool_idx            = fd_bank_##name##_pool_idx_null( name##_pool ); \
     447          48 :     bank->name##_dirty               = 0;
     448             : 
     449             :   /* Do nothing for these. */
     450           6 :   #define HAS_COW_0(name)
     451             : 
     452           6 :   #define HAS_LOCK_1(name) \
     453          48 :     fd_rwlock_unwrite(&bank->name##_lock);
     454           6 :   #define HAS_LOCK_0(name)
     455             : 
     456           6 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     457          48 :     HAS_COW_##cow(name);                                 \
     458         330 :     HAS_LOCK_##has_lock(name)
     459         330 :   FD_BANKS_ITER(X)
     460           6 :   #undef X
     461           6 :   #undef HAS_COW_0
     462           6 :   #undef HAS_COW_1
     463           6 :   #undef HAS_LOCK_0
     464           6 :   #undef HAS_LOCK_1
     465             : 
     466           6 :   fd_banks_map_ele_insert( bank_map, bank, bank_pool );
     467             : 
     468             :   /* Now that the node is inserted, update the root */
     469             : 
     470           6 :   banks->root     = slot;
     471           6 :   banks->root_idx = fd_banks_pool_idx( bank_pool, bank );
     472             : 
     473           6 :   return bank;
     474           6 : }
     475             : 
     476             : fd_bank_t *
     477          15 : fd_banks_get_bank( fd_banks_t * banks, ulong slot ) {
     478             : 
     479          15 :   fd_bank_t *      bank_pool = fd_banks_get_bank_pool( banks );
     480          15 :   fd_banks_map_t * bank_map  = fd_banks_get_bank_map( banks );
     481             : 
     482          15 :   ulong idx = fd_banks_map_idx_query_const( bank_map, &slot, ULONG_MAX, bank_pool );
     483          15 :   if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
     484           6 :     FD_LOG_WARNING(( "Failed to get bank idx for slot %lu", slot ));
     485           6 :     return NULL;
     486           6 :   }
     487             : 
     488           9 :   fd_bank_t * bank = fd_banks_pool_ele( bank_pool, idx );
     489           9 :   if( FD_UNLIKELY( !bank ) ) {
     490           0 :     FD_LOG_WARNING(( "Failed to get bank for slot %lu", slot ));
     491           0 :     return NULL;
     492           0 :   }
     493             : 
     494           9 :   return bank;
     495           9 : }
     496             : 
     497             : 
     498             : fd_bank_t *
     499             : fd_banks_clone_from_parent( fd_banks_t * banks,
     500             :                             ulong        slot,
     501          30 :                             ulong        parent_slot ) {
     502             : 
     503          30 :   fd_rwlock_write( &banks->rwlock );
     504             : 
     505          30 :   fd_bank_t *      bank_pool = fd_banks_get_bank_pool( banks );
     506          30 :   fd_banks_map_t * bank_map  = fd_banks_get_bank_map( banks );
     507             : 
     508             :   /* See if we already recovered the bank */
     509             : 
     510          30 :   fd_bank_t * old_bank = fd_banks_map_ele_query( bank_map, &slot, NULL, bank_pool );
     511          30 :   if( FD_UNLIKELY( !!old_bank ) ) {
     512           0 :     FD_LOG_CRIT(( "Invariant violation: bank for slot %lu already exists", slot ));
     513           0 :   }
     514             : 
     515             :   /* First query for the parent bank */
     516             : 
     517          30 :   fd_bank_t * parent_bank = fd_banks_map_ele_query( bank_map, &parent_slot, NULL, bank_pool );
     518             : 
     519          30 :   if( FD_UNLIKELY( !parent_bank ) ) {
     520           0 :     FD_LOG_WARNING(( "Failed to get bank for parent slot %lu", parent_slot ));
     521           0 :     fd_rwlock_unwrite( &banks->rwlock );
     522           0 :     return NULL;
     523           0 :   }
     524             : 
     525          30 :   if( FD_UNLIKELY( fd_bank_slot_get( parent_bank ) != parent_slot ) ) {
     526           0 :     FD_LOG_WARNING(( "Parent slot mismatch" ));
     527           0 :     fd_rwlock_unwrite( &banks->rwlock );
     528           0 :     return NULL;
     529           0 :   }
     530             : 
     531          30 :   ulong parent_idx = fd_banks_pool_idx( bank_pool, parent_bank );
     532             : 
     533             :   /* Now acquire a new bank */
     534             : 
     535          30 :   FD_LOG_NOTICE(( "slot: %lu, fd_banks_pool_max: %lu, fd_banks_pool_free: %lu", slot, fd_banks_pool_max( bank_pool ), fd_banks_pool_free( bank_pool ) ));
     536             : 
     537          30 :   if( FD_UNLIKELY( !fd_banks_pool_free( bank_pool ) ) ) {
     538           0 :     FD_LOG_WARNING(( "No free banks" ));
     539           0 :     fd_rwlock_unwrite( &banks->rwlock );
     540           0 :     return NULL;
     541           0 :   }
     542             : 
     543          30 :   fd_bank_t * new_bank = fd_banks_pool_ele_acquire( bank_pool );
     544          30 :   if( FD_UNLIKELY( !new_bank ) ) {
     545           0 :     FD_LOG_WARNING(( "Failed to acquire bank" ));
     546           0 :     fd_rwlock_unwrite( &banks->rwlock );
     547           0 :     return NULL;
     548           0 :   }
     549             : 
     550          30 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
     551             : 
     552          30 :   new_bank->slot_       = slot;
     553          30 :   new_bank->next        = null_idx;
     554          30 :   new_bank->parent_idx  = null_idx;
     555          30 :   new_bank->child_idx   = null_idx;
     556          30 :   new_bank->sibling_idx = null_idx;
     557             : 
     558          30 :   fd_banks_map_ele_insert( bank_map, new_bank, bank_pool );
     559             : 
     560          30 :   ulong child_idx = fd_banks_pool_idx( bank_pool, new_bank );
     561             : 
     562             :   /* Link node->parent */
     563             : 
     564          30 :   new_bank->parent_idx = parent_idx;
     565             : 
     566             :   /* Link parent->node and sibling->node */
     567             : 
     568          30 :   if( FD_LIKELY( parent_bank->child_idx == null_idx ) ) {
     569             : 
     570             :     /* This is the first child so set as left-most child */
     571             : 
     572          18 :     parent_bank->child_idx = child_idx;
     573             : 
     574          18 :   } else {
     575             : 
     576             :     /* Already have children so iterate to right-most sibling. */
     577             : 
     578          12 :     fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, parent_bank->child_idx );
     579          15 :     while( curr_bank->sibling_idx != null_idx ) curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->sibling_idx );
     580             : 
     581             :     /* Link to right-most sibling. */
     582             : 
     583          12 :     curr_bank->sibling_idx = child_idx;
     584             : 
     585          12 :   }
     586             : 
     587             :   /* We want to copy over the fields from the parent to the child,
     588             :      except for the fields which correspond to the header of the bank
     589             :      struct which is used for pool and map management. We can take
     590             :      advantage of the fact that those fields are laid out at the top
     591             :      of the bank struct. */
     592             : 
     593          30 :   memcpy( (uchar *)new_bank + FD_BANK_HEADER_SIZE, (uchar *)parent_bank + FD_BANK_HEADER_SIZE, sizeof(fd_bank_t) - FD_BANK_HEADER_SIZE );
     594             : 
     595             :   /* Setup all of the CoW fields. */
     596          30 :   #define HAS_COW_1(name)                                                   \
     597         240 :     new_bank->name##_pool_idx        = parent_bank->name##_pool_idx;        \
     598         240 :     new_bank->name##_dirty           = 0UL;                                 \
     599         240 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
     600         240 :     fd_bank_set_##name##_pool( new_bank, name##_pool );
     601             : 
     602             :   /* Do nothing if not CoW. */
     603          30 :   #define HAS_COW_0(name)
     604             : 
     605             :   /* Setup locks for new bank as free. */
     606          30 :   #define HAS_LOCK_1(name) \
     607         240 :     fd_rwlock_unwrite(&new_bank->name##_lock);
     608          30 :   #define HAS_LOCK_0(name)
     609             : 
     610          30 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     611         240 :     HAS_COW_##cow(name);                                 \
     612        1650 :     HAS_LOCK_##has_lock(name)
     613        1650 :   FD_BANKS_ITER(X)
     614          30 :   #undef X
     615          30 :   #undef HAS_COW_0
     616          30 :   #undef HAS_COW_1
     617          30 :   #undef HAS_LOCK_0
     618          30 :   #undef HAS_LOCK_1
     619             : 
     620          30 :   fd_rwlock_unwrite( &banks->rwlock );
     621             : 
     622          30 :   return new_bank;
     623          30 : }
     624             : 
     625             : fd_bank_t const *
     626           3 : fd_banks_publish( fd_banks_t * banks, ulong slot ) {
     627             : 
     628           3 :   fd_rwlock_write( &banks->rwlock );
     629             : 
     630           3 :   fd_bank_t *      bank_pool = fd_banks_get_bank_pool( banks );
     631           3 :   fd_banks_map_t * bank_map  = fd_banks_get_bank_map( banks );
     632             : 
     633           3 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
     634             : 
     635             :   /* We want to replace the old root with the new root. This means we
     636             :      have to remove banks that aren't descendants of the new root. */
     637             : 
     638           3 :   fd_bank_t const * old_root = fd_banks_root( banks );
     639           3 :   if( FD_UNLIKELY( !old_root ) ) {
     640           0 :     FD_LOG_WARNING(( "Failed to get root bank" ));
     641           0 :     fd_rwlock_unwrite( &banks->rwlock );
     642           0 :     return NULL;
     643           0 :   }
     644             : 
     645           3 :   fd_bank_t * new_root = fd_banks_map_ele_query( bank_map, &slot, NULL, bank_pool );
     646           3 :   if( FD_UNLIKELY( !new_root ) ) {
     647           0 :     FD_LOG_WARNING(( "Failed to get new root bank" ));
     648           0 :     fd_rwlock_unwrite( &banks->rwlock );
     649           0 :     return NULL;
     650           0 :   }
     651             : 
     652           3 :   fd_bank_t * head = fd_banks_map_ele_remove( bank_map, &old_root->slot_, NULL, bank_pool );
     653           3 :   head->next       = fd_banks_pool_idx_null( bank_pool );
     654           3 :   fd_bank_t * tail = head;
     655             : 
     656          21 :   while( head ) {
     657          18 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, head->child_idx );
     658             : 
     659          36 :     while( FD_LIKELY( child ) ) {
     660             : 
     661          18 :       if( FD_LIKELY( child!=new_root ) ) {
     662             : 
     663             :         /* Remove the child from the map first and push onto the
     664             :            frontier list that needs to be iterated through */
     665          15 :         tail->next = fd_banks_map_idx_remove(
     666          15 :             bank_map,
     667          15 :             &child->slot_,
     668          15 :             fd_banks_pool_idx_null( bank_pool ),
     669          15 :             bank_pool );
     670             : 
     671          15 :         tail       = fd_banks_pool_ele( bank_pool, tail->next );
     672          15 :         tail->next = fd_banks_pool_idx_null( bank_pool );
     673             : 
     674          15 :       }
     675             : 
     676          18 :       child = fd_banks_pool_ele( bank_pool, child->sibling_idx );
     677          18 :     }
     678             : 
     679          18 :     fd_bank_t * next = fd_banks_pool_ele( bank_pool, head->next );
     680             : 
     681             :     /* Decide if we need to free any CoW fields. We free a CoW member
     682             :        from its pool if the dirty flag is set unless it is the same
     683             :        pool that the new root uses. */
     684          18 :     #define HAS_COW_1(name)                                                          \
     685         288 :       if( head->name##_dirty && head->name##_pool_idx!=new_root->name##_pool_idx ) { \
     686           3 :         fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );      \
     687           3 :         fd_bank_##name##_pool_idx_release( name##_pool, head->name##_pool_idx );     \
     688           3 :       }
     689             :     /* Do nothing for these. */
     690          18 :     #define HAS_COW_0(name)
     691             : 
     692          18 :     #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     693         144 :       HAS_COW_##cow(name)
     694         144 :     FD_BANKS_ITER(X)
     695          18 :     #undef X
     696          18 :     #undef HAS_COW_0
     697          18 :     #undef HAS_COW_1
     698             : 
     699          18 :     fd_banks_pool_ele_release( bank_pool, head );
     700          18 :     head = next;
     701          18 :   }
     702             : 
     703             :   /* If the new root did not have the dirty bit set, that means the node
     704             :      didn't own the pool index. Change the ownership to the new root. */
     705           3 :   #define HAS_COW_1(name)                                                            \
     706          24 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );          \
     707          24 :     if( new_root->name##_pool_idx!=fd_bank_##name##_pool_idx_null( name##_pool ) ) { \
     708           3 :       new_root->name##_dirty = 1;                                                    \
     709           3 :     }
     710             :   /* Do nothing if not CoW. */
     711           3 :   #define HAS_COW_0(name)
     712             : 
     713           3 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     714          24 :     HAS_COW_##cow(name)
     715          24 :   FD_BANKS_ITER(X)
     716           3 :   #undef X
     717           3 :   #undef HAS_COW_0
     718           3 :   #undef HAS_COW_1
     719             : 
     720           3 :   new_root->parent_idx = null_idx;
     721           3 :   banks->root_idx      = fd_banks_map_idx_query( bank_map, &slot, null_idx, bank_pool );
     722           3 :   banks->root          = slot;
     723             : 
     724           3 :   fd_rwlock_unwrite( &banks->rwlock );
     725             : 
     726           3 :   return new_root;
     727           3 : }
     728             : 
     729             : void
     730           3 : fd_banks_clear_bank( fd_banks_t * banks, fd_bank_t * bank ) {
     731             : 
     732             :   /* Get the parent bank. */
     733           3 :   fd_bank_t * parent_bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank->parent_idx );
     734             : 
     735           3 :   #define HAS_COW_1(type, name, footprint)                                                                                  \
     736          24 :     fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank );                                                   \
     737          24 :     if( bank->name##_dirty ) {                                                                                              \
     738             :       /* If the dirty flag is set, then we have a pool allocated for */                                                     \
     739             :       /* this specific bank. We need to release the pool index and   */                                                     \
     740             :       /* assign the bank to the idx corresponding to the parent.     */                                                     \
     741           6 :       fd_bank_##name##_pool_idx_release( name##_pool, bank->name##_pool_idx );                                              \
     742           6 :       bank->name##_dirty    = 0;                                                                                            \
     743           6 :       bank->name##_pool_idx = !!parent_bank ? parent_bank->name##_pool_idx : fd_bank_##name##_pool_idx_null( name##_pool ); \
     744           6 :     }
     745             : 
     746           3 :   #define HAS_COW_0(type, name, footprint) \
     747         141 :     fd_memset( bank->name, 0, footprint );
     748             : 
     749           3 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     750         165 :     HAS_COW_##cow(type, name, footprint)
     751         165 :   FD_BANKS_ITER(X)
     752           3 :   #undef X
     753           3 :   #undef HAS_COW_0
     754           3 :   #undef HAS_COW_1
     755           3 : }
     756             : 
     757             : fd_bank_t *
     758          12 : fd_banks_rekey_root_bank( fd_banks_t * banks, ulong slot ) {
     759             : 
     760          12 :   if( FD_UNLIKELY( !banks ) ) {
     761           0 :     FD_LOG_WARNING(( "Banks is NULL" ));
     762           0 :     return NULL;
     763           0 :   }
     764             : 
     765          12 :   if( FD_UNLIKELY( banks->root_idx==fd_banks_pool_idx_null( fd_banks_get_bank_pool( banks ) ) ) ) {
     766           0 :     FD_LOG_WARNING(( "Root bank does not exist" ));
     767           0 :     return NULL;
     768           0 :   }
     769             : 
     770          12 :   fd_bank_t * bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), banks->root_idx );
     771          12 :   if( FD_UNLIKELY( !bank ) ) {
     772           0 :     FD_LOG_WARNING(( "Failed to get root bank" ));
     773           0 :     return NULL;
     774           0 :   }
     775             : 
     776             :   /* Once we validated that there is a valid root bank, we can remove
     777             :      the bank from the map and insert it with the new key. */
     778          12 :   bank = fd_banks_map_ele_remove( fd_banks_get_bank_map( banks ), &bank->slot_, NULL, fd_banks_get_bank_pool( banks ) );
     779          12 :   if( FD_UNLIKELY( !bank ) ) {
     780           3 :     FD_LOG_WARNING(( "Failed to remove root bank" ));
     781           3 :     return NULL;
     782           3 :   }
     783             : 
     784           9 :   bank->slot_ = slot;
     785             : 
     786           9 :   if( FD_UNLIKELY( !fd_banks_map_ele_insert( fd_banks_get_bank_map( banks ), bank, fd_banks_get_bank_pool( banks ) ) ) ) {
     787           0 :     FD_LOG_WARNING(( "Failed to insert root bank" ));
     788           0 :     return NULL;
     789           0 :   }
     790             : 
     791           9 :   return bank;
     792           9 : }

Generated by: LCOV version 1.14