LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 559 768 72.8 %
Date: 2025-10-13 04:42:14 Functions: 48 213 22.5 %

          Line data    Source code
       1             : #include "fd_bank.h"
       2             : #include "fd_runtime_const.h"
       3             : 
       4             : ulong
       5           0 : fd_bank_align( void ) {
       6           0 :   return alignof(fd_bank_t);
       7           0 : }
       8             : 
       9             : ulong
      10           0 : fd_bank_footprint( void ) {
      11           0 :   ulong l = FD_LAYOUT_INIT;
      12           0 :   l = FD_LAYOUT_APPEND( l, fd_bank_align(), sizeof(fd_bank_t) );
      13           0 :   return FD_LAYOUT_FINI( l, fd_bank_align() );
      14           0 : }
      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           3 :       fd_bank_##name##_t * bank_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx );          \
      49           3 :       return (type *)bank_##name->data;                                                                            \
      50           3 :     }                                                                                                              \
      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          18 :     fd_rwlock_write( fd_bank_get_##name##_pool_lock( bank ) );                                                     \
      55          18 :     fd_bank_##name##_t * child_##name = fd_bank_##name##_pool_ele_acquire( name##_pool );                          \
      56          18 :     if( FD_UNLIKELY( !child_##name ) ) {                                                                           \
      57           0 :       FD_LOG_CRIT(( "Failed to acquire " #name " pool element" ));                                                 \
      58           0 :     }                                                                                                              \
      59          18 :     fd_rwlock_unwrite( fd_bank_get_##name##_pool_lock( bank ) );                                                   \
      60          18 :     /* If the dirty flag has not been set yet, we need to allocated a */                                           \
      61          18 :     /* new pool element and copy over the data from the parent idx.   */                                           \
      62          18 :     /* We also need to mark the dirty flag. */                                                                     \
      63          18 :     ulong child_idx = fd_bank_##name##_pool_idx( name##_pool, child_##name );                                      \
      64          18 :     if( bank->name##_pool_idx!=fd_bank_##name##_pool_idx_null( name##_pool ) ) {                                   \
      65           6 :       fd_bank_##name##_t * parent_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx );        \
      66           6 :       fd_memcpy( child_##name->data, parent_##name->data, fd_bank_##name##_footprint );                            \
      67           6 :     }                                                                                                              \
      68          18 :     bank->name##_pool_idx = child_idx;                                                                             \
      69          18 :     bank->name##_dirty    = 1;                                                                                     \
      70          18 :     return (type *)child_##name->data;                                                                             \
      71          18 :   }                                                                                                                \
      72             :   void                                                                                                             \
      73          21 :   fd_bank_##name##_end_locking_modify( fd_bank_t * bank ) {                                                        \
      74          21 :     fd_rwlock_unwrite( &bank->name##_lock );                                                                       \
      75          21 :   }
      76             : 
      77             : #define HAS_LOCK_0(type, name)                                    \
      78             :   type const *                                                    \
      79        1500 :   fd_bank_##name##_query( fd_bank_t const * bank ) {              \
      80        1500 :     return (type const *)fd_type_pun_const( bank->non_cow.name ); \
      81        1500 :   }                                                               \
      82             :   type *                                                          \
      83         912 :   fd_bank_##name##_modify( fd_bank_t * bank ) {                   \
      84         912 :     return (type *)fd_type_pun( bank->non_cow.name );             \
      85         912 :   }
      86             : 
      87             : #define HAS_LOCK_1(type, name)                                    \
      88             :   type const *                                                    \
      89           0 :   fd_bank_##name##_locking_query( fd_bank_t * bank ) {            \
      90           0 :     fd_rwlock_read( &bank->name##_lock );                         \
      91           0 :     return (type const *)fd_type_pun_const( bank->non_cow.name ); \
      92           0 :   }                                                               \
      93             :   type *                                                          \
      94         462 :   fd_bank_##name##_locking_modify( fd_bank_t * bank ) {           \
      95         462 :     fd_rwlock_write( &bank->name##_lock );                        \
      96         462 :     return (type *)fd_type_pun( bank->non_cow.name );             \
      97         462 :   }                                                               \
      98             :   void                                                            \
      99           0 :   fd_bank_##name##_end_locking_query( fd_bank_t * bank ) {        \
     100           0 :     fd_rwlock_unread( &bank->name##_lock );                       \
     101           0 :   }                                                               \
     102             :   void                                                            \
     103         462 :   fd_bank_##name##_end_locking_modify( fd_bank_t * bank ) {       \
     104         462 :     fd_rwlock_unwrite( &bank->name##_lock );                      \
     105         462 :   }
     106             : 
     107             : #define HAS_COW_0(type, name, footprint, align, has_lock) \
     108             :   HAS_LOCK_##has_lock(type, name)                         \
     109             :   void                                                    \
     110        1041 :   fd_bank_##name##_set( fd_bank_t * bank, type value ) {  \
     111        1041 :     FD_STORE( type, bank->non_cow.name, value );          \
     112        1041 :   }                                                       \
     113             :   type                                                    \
     114        1245 :   fd_bank_##name##_get( fd_bank_t const * bank ) {        \
     115        1245 :     type val = FD_LOAD( type, bank->non_cow.name );       \
     116        1245 :     return val;                                           \
     117        1245 :   }
     118             : 
     119             : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     120             :   HAS_COW_##cow(type, name, footprint, align, has_lock)
     121           6 : FD_BANKS_ITER(X)
     122           6 : #undef X
     123           6 : #undef HAS_COW_0
     124           6 : #undef HAS_COW_1
     125           6 : #undef HAS_LOCK_0
     126           6 : #undef HAS_LOCK_1
     127           6 : 
     128           6 : /**********************************************************************/
     129           6 : 
     130           6 : ulong
     131         192 : fd_banks_align( void ) {
     132             :   /* TODO: The magic number here can probably be removed. */
     133         192 :   return 128UL;
     134         192 : }
     135             : 
     136             : ulong
     137             : fd_banks_footprint( ulong max_total_banks,
     138          18 :                     ulong max_fork_width ) {
     139             : 
     140             :   /* max_fork_width is used in the macro below. */
     141             : 
     142          18 :   ulong l = FD_LAYOUT_INIT;
     143          18 :   l = FD_LAYOUT_APPEND( l, fd_banks_align(),                  sizeof(fd_banks_t) );
     144          18 :   l = FD_LAYOUT_APPEND( l, fd_banks_pool_align(),             fd_banks_pool_footprint( max_total_banks ) );
     145          18 :   l = FD_LAYOUT_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
     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          18 :   #define HAS_COW_1_LIMIT_1(name) \
     151          72 :     l = FD_LAYOUT_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_fork_width ) );
     152             : 
     153          18 :   #define HAS_COW_1_LIMIT_0(name) \
     154          18 :     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          18 :   #define HAS_COW_0_LIMIT_0(name)
     158             : 
     159          18 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock)  \
     160          90 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     161          90 :   FD_BANKS_ITER(X)
     162          18 :   #undef X
     163          18 :   #undef HAS_COW_0_LIMIT_0
     164          18 :   #undef HAS_COW_1_LIMIT_0
     165          18 :   #undef HAS_COW_1_LIMIT_1
     166             : 
     167          18 :   return FD_LAYOUT_FINI( l, fd_banks_align() );
     168          18 : }
     169             : 
     170             : void *
     171           9 : fd_banks_new( void * shmem, ulong max_total_banks, ulong max_fork_width ) {
     172             : 
     173           9 :   fd_banks_t * banks = (fd_banks_t *)shmem;
     174             : 
     175           9 :   if( FD_UNLIKELY( !banks ) ) {
     176           0 :     FD_LOG_WARNING(( "NULL banks" ));
     177           0 :     return NULL;
     178           0 :   }
     179             : 
     180           9 :   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           9 :   fd_rwlock_unwrite( &banks->rwlock );
     187             : 
     188             :   /* First, layout the banks and the pool used by fd_banks_t. */
     189           9 :   FD_SCRATCH_ALLOC_INIT( l, banks );
     190           9 :   banks                        = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),                  sizeof(fd_banks_t) );
     191           9 :   void * pool_mem              = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(),             fd_banks_pool_footprint( max_total_banks ) );
     192           9 :   void * cost_tracker_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
     193             : 
     194             :   /* Need to layout all of the CoW pools. */
     195           0 :   #define HAS_COW_1_LIMIT_1(name) \
     196          36 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_fork_width ) );
     197             : 
     198           0 :   #define HAS_COW_1_LIMIT_0(name) \
     199           9 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_total_banks ) );
     200             : 
     201             :   /* Do nothing for these. */
     202           0 :   #define HAS_COW_0_LIMIT_0(name)
     203             : 
     204           0 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     205          45 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     206          45 :   FD_BANKS_ITER(X)
     207           0 :   #undef X
     208           0 :   #undef HAS_COW_0_LIMIT_0
     209           0 :   #undef HAS_COW_1_LIMIT_0
     210           0 :   #undef HAS_COW_1_LIMIT_1
     211             : 
     212           9 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() ) != (ulong)banks + fd_banks_footprint( max_total_banks, max_fork_width ) ) ) {
     213           0 :     FD_LOG_WARNING(( "fd_banks_new: bad layout" ));
     214           0 :     return NULL;
     215           0 :   }
     216             : 
     217           9 :   void * pool = fd_banks_pool_new( pool_mem, max_total_banks );
     218           9 :   if( FD_UNLIKELY( !pool ) ) {
     219           0 :     FD_LOG_WARNING(( "Failed to create bank pool" ));
     220           0 :     return NULL;
     221           0 :   }
     222             : 
     223           9 :   fd_bank_t * bank_pool = fd_banks_pool_join( pool );
     224           9 :   if( FD_UNLIKELY( !bank_pool ) ) {
     225           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     226           0 :     return NULL;
     227           0 :   }
     228             : 
     229             :   /* Mark all of the banks as not initialized. */
     230         108 :   for( ulong i=0UL; i<max_total_banks; i++ ) {
     231          99 :     fd_bank_t * bank = fd_banks_pool_ele( bank_pool, i );
     232          99 :     if( FD_UNLIKELY( !bank ) ) {
     233           0 :       FD_LOG_WARNING(( "Failed to get bank" ));
     234           0 :       return NULL;
     235           0 :     }
     236          99 :     bank->flags = 0UL;
     237          99 :   }
     238             : 
     239           9 :   fd_banks_set_bank_pool( banks, bank_pool );
     240             : 
     241             :   /* Now call _new() and _join for the cost tracker pool.  Also, update
     242             :      the offset in the banks. */
     243             : 
     244           9 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_cost_tracker_pool_join( fd_bank_cost_tracker_pool_new( cost_tracker_pool_mem, max_fork_width ) );
     245           9 :   if( FD_UNLIKELY( !cost_tracker_pool ) ) {
     246           0 :     FD_LOG_WARNING(( "Failed to new or join cost tracker pool" ));
     247           0 :     return NULL;
     248           0 :   }
     249           9 :   fd_banks_set_cost_tracker_pool( banks, cost_tracker_pool );
     250             : 
     251             :   /* Now, call _new() and _join() for all of the CoW pools. */
     252           9 :   #define HAS_COW_1_LIMIT_1(name)                                                     \
     253          36 :     fd_rwlock_unwrite( &banks->name##_pool_lock );                                    \
     254          36 :     void * name##_mem = fd_bank_##name##_pool_new( name##_pool_mem, max_fork_width ); \
     255          36 :     if( FD_UNLIKELY( !name##_mem ) ) {                                                \
     256           0 :       FD_LOG_WARNING(( "Failed to create " #name " pool" ));                          \
     257           0 :       return NULL;                                                                    \
     258           0 :     }                                                                                 \
     259          36 :     fd_bank_##name##_t * name##_pool = fd_bank_##name##_pool_join( name##_pool_mem ); \
     260          36 :     if( FD_UNLIKELY( !name##_pool ) ) {                                               \
     261           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                            \
     262           0 :       return NULL;                                                                    \
     263           0 :     }                                                                                 \
     264          36 :     fd_banks_set_##name##_pool( banks, name##_pool );
     265             : 
     266           9 :   #define HAS_COW_1_LIMIT_0(name)                                                      \
     267           9 :     fd_rwlock_unwrite( &banks->name##_pool_lock );                                     \
     268           9 :     void * name##_mem = fd_bank_##name##_pool_new( name##_pool_mem, max_total_banks ); \
     269           9 :     if( FD_UNLIKELY( !name##_mem ) ) {                                                 \
     270           0 :       FD_LOG_WARNING(( "Failed to create " #name " pool" ));                           \
     271           0 :       return NULL;                                                                     \
     272           0 :     }                                                                                  \
     273           9 :     fd_bank_##name##_t * name##_pool = fd_bank_##name##_pool_join( name##_pool_mem );  \
     274           9 :     if( FD_UNLIKELY( !name##_pool ) ) {                                                \
     275           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                             \
     276           0 :       return NULL;                                                                     \
     277           0 :     }                                                                                  \
     278           9 :     fd_banks_set_##name##_pool( banks, name##_pool );
     279             : 
     280             :   /* Do nothing for these. */
     281           9 :   #define HAS_COW_0_LIMIT_0(name)
     282             : 
     283           9 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     284          45 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     285          90 :   FD_BANKS_ITER(X)
     286          90 :   #undef X
     287          90 :   #undef HAS_COW_0_LIMIT_0
     288          90 :   #undef HAS_COW_1_LIMIT_1
     289          90 :   #undef HAS_COW_1_LIMIT_0
     290             : 
     291             :   /* Now we need to assign offsets for all of the pools for each
     292             :      fd_bank_t. */
     293             : 
     294         108 :   for( ulong i=0UL; i<max_total_banks; i++ ) {
     295             : 
     296          99 :     fd_bank_t * bank = fd_banks_pool_ele( bank_pool, i );
     297          99 :     #define HAS_COW_1(name)                                                   \
     298         495 :       fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
     299         495 :       fd_bank_set_##name##_pool( bank, name##_pool );                         \
     300         495 :       fd_bank_set_##name##_pool_lock( bank, &banks->name##_pool_lock );
     301          99 :     #define HAS_COW_0(name)
     302             : 
     303          99 :     #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     304         495 :     HAS_COW_##cow(name)
     305         495 :     FD_BANKS_ITER(X)
     306          99 :     #undef X
     307          99 :     #undef HAS_COW_0
     308          99 :     #undef HAS_COW_1
     309             : 
     310             :     /* The cost tracker is not templatized and must be set manually. */
     311          99 :     fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
     312          99 :     fd_bank_set_cost_tracker_pool( bank, cost_tracker_pool );
     313          99 :   }
     314             : 
     315          90 :   banks->max_total_banks = max_total_banks;
     316          90 :   banks->max_fork_width  = max_fork_width;
     317          90 :   banks->root_idx        = ULONG_MAX;
     318             : 
     319          90 :   if( FD_UNLIKELY( !fd_stake_delegations_new( banks->stake_delegations_root, FD_RUNTIME_MAX_STAKE_ACCOUNTS, 0 ) ) ) {
     320           0 :     FD_LOG_WARNING(( "Unable to create stake delegations root" ));
     321           0 :     return NULL;
     322           0 :   }
     323             : 
     324           9 :   FD_COMPILER_MFENCE();
     325           9 :   FD_VOLATILE( banks->magic ) = FD_BANKS_MAGIC;
     326           9 :   FD_COMPILER_MFENCE();
     327             : 
     328           9 :   return shmem;
     329          90 : }
     330             : 
     331             : fd_banks_t *
     332          15 : fd_banks_join( void * mem ) {
     333          15 :   fd_banks_t * banks = (fd_banks_t *)mem;
     334             : 
     335          15 :   if( FD_UNLIKELY( !banks ) ) {
     336           0 :     FD_LOG_WARNING(( "NULL banks" ));
     337           0 :     return NULL;
     338           0 :   }
     339             : 
     340          15 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks, fd_banks_align() ) ) ) {
     341           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     342           0 :     return NULL;
     343           0 :   }
     344             : 
     345          15 :   if( FD_UNLIKELY( banks->magic!=FD_BANKS_MAGIC ) ) {
     346           3 :     FD_LOG_WARNING(( "Invalid banks magic" ));
     347           3 :     return NULL;
     348           3 :   }
     349             : 
     350          12 :   FD_SCRATCH_ALLOC_INIT( l, banks );
     351          12 :   banks                         = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),                  sizeof(fd_banks_t) );
     352          12 :   void * pool_mem               = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(),             fd_banks_pool_footprint( banks->max_total_banks ) );
     353          12 :   void * cost_tracker_pool_mem  = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( banks->max_fork_width ) );
     354             : 
     355             :   /* Need to layout all of the CoW pools. */
     356           0 :   #define HAS_COW_1_LIMIT_1(name) \
     357          48 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( banks->max_fork_width ) );
     358             : 
     359           0 :   #define HAS_COW_1_LIMIT_0(name) \
     360          12 :     void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( banks->max_total_banks ) );
     361             : 
     362             :   /* Don't need to layout if not CoW. */
     363           0 :   #define HAS_COW_0_LIMIT_0(name)
     364             : 
     365           0 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     366          60 :     HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
     367          60 :   FD_BANKS_ITER(X)
     368           0 :   #undef X
     369           0 :   #undef HAS_COW_0_LIMIT_0
     370           0 :   #undef HAS_COW_1_LIMIT_0
     371           0 :   #undef HAS_COW_1_LIMIT_1
     372             : 
     373          12 :   FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() );
     374             : 
     375          60 :   fd_bank_t * banks_pool = fd_banks_get_bank_pool( banks );
     376          60 :   if( FD_UNLIKELY( !banks_pool ) ) {
     377           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     378           0 :     return NULL;
     379           0 :   }
     380             : 
     381          12 :   if( FD_UNLIKELY( banks_pool!=fd_banks_pool_join( pool_mem ) ) ) {
     382           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     383           0 :     return NULL;
     384           0 :   }
     385             : 
     386          12 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
     387          12 :   if( FD_UNLIKELY( !cost_tracker_pool ) ) {
     388           0 :     FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
     389           0 :     return NULL;
     390           0 :   }
     391             : 
     392          12 :   if( FD_UNLIKELY( cost_tracker_pool!=fd_bank_cost_tracker_pool_join( cost_tracker_pool_mem ) ) ) {
     393           0 :     FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
     394           0 :     return NULL;
     395           0 :   }
     396             : 
     397             :   /* Now, call _join() for all of the CoW pools. */
     398          12 :   #define HAS_COW_1(name)                                                             \
     399          60 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );           \
     400          60 :     if( FD_UNLIKELY( !name##_pool ) ) {                                               \
     401           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                            \
     402           0 :       return NULL;                                                                    \
     403           0 :     }                                                                                 \
     404          60 :     if( FD_UNLIKELY( name##_pool!=fd_bank_##name##_pool_join( name##_pool_mem ) ) ) { \
     405           0 :       FD_LOG_WARNING(( "Failed to join " #name " pool" ));                            \
     406           0 :       return NULL;                                                                    \
     407           0 :     }
     408             : 
     409             :   /* Do nothing when the field is not CoW. */
     410          12 :   #define HAS_COW_0(name)
     411             : 
     412          12 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     413          60 :     HAS_COW_##cow(name)
     414         120 :   FD_BANKS_ITER(X)
     415          12 :   #undef X
     416          12 :   #undef HAS_COW_0
     417          12 :   #undef HAS_COW_1
     418             : 
     419          12 :   return banks;
     420         120 : }
     421             : 
     422             : void *
     423           9 : fd_banks_leave( fd_banks_t * banks ) {
     424             : 
     425           9 :   if( FD_UNLIKELY( !banks ) ) {
     426           0 :     FD_LOG_WARNING(( "NULL banks" ));
     427           0 :     return NULL;
     428           0 :   }
     429             : 
     430           9 :   return (void *)banks;
     431           9 : }
     432             : 
     433             : void *
     434           3 : fd_banks_delete( void * shmem ) {
     435             : 
     436           3 :   if( FD_UNLIKELY( !shmem ) ) {
     437           0 :     FD_LOG_WARNING(( "NULL banks" ));
     438           0 :     return NULL;
     439           0 :   }
     440             : 
     441           3 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shmem, fd_banks_align() ) ) ) {
     442           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     443           0 :     return NULL;
     444           0 :   }
     445             : 
     446           3 :   fd_banks_t * banks = (fd_banks_t *)shmem;
     447           3 :   banks->magic = 0UL;
     448             : 
     449           3 :   return shmem;
     450           3 : }
     451             : 
     452             : fd_bank_t *
     453           9 : fd_banks_init_bank( fd_banks_t * banks ) {
     454             : 
     455           9 :   if( FD_UNLIKELY( !banks ) ) {
     456           0 :     FD_LOG_WARNING(( "NULL banks" ));
     457           0 :     return NULL;
     458           0 :   }
     459             : 
     460           9 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     461             : 
     462           9 :   fd_rwlock_write( &banks->rwlock );
     463             : 
     464           9 :   fd_bank_t * bank = fd_banks_pool_ele_acquire( bank_pool );
     465           9 :   if( FD_UNLIKELY( bank==NULL ) ) {
     466           0 :     FD_LOG_WARNING(( "Failed to acquire bank" ));
     467           0 :     fd_rwlock_unwrite( &banks->rwlock );
     468           0 :     return NULL;
     469           0 :   }
     470             : 
     471           9 :   #define HAS_COW_1(type, name, footprint) \
     472          45 :     bank->name##_dirty    = 0;             \
     473          45 :     bank->name##_pool_idx = fd_bank_##name##_pool_idx_null( fd_bank_get_##name##_pool( bank ) );
     474             : 
     475           9 :   #define HAS_COW_0(type, name, footprint) \
     476         378 :     fd_memset( bank->non_cow.name, 0, footprint );
     477             : 
     478           9 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     479         423 :     HAS_COW_##cow(type, name, footprint)
     480         423 :   FD_BANKS_ITER(X)
     481           9 :   #undef X
     482           9 :   #undef HAS_COW_0
     483           9 :   #undef HAS_COW_1
     484             : 
     485           9 :   ulong null_idx    = fd_banks_pool_idx_null( bank_pool );
     486           9 :   bank->idx         = fd_banks_pool_idx( bank_pool, bank );
     487           9 :   bank->next        = null_idx;
     488           9 :   bank->parent_idx  = null_idx;
     489           9 :   bank->child_idx   = null_idx;
     490           9 :   bank->sibling_idx = null_idx;
     491             : 
     492             :   /* Set all CoW fields to null. */
     493           9 :   #define HAS_COW_1(name)                                                             \
     494          45 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );           \
     495          45 :     bank->name##_pool_idx            = fd_bank_##name##_pool_idx_null( name##_pool ); \
     496          45 :     bank->name##_dirty               = 0;
     497             : 
     498             :   /* Do nothing for these. */
     499           9 :   #define HAS_COW_0(name)
     500             : 
     501           9 :   #define HAS_LOCK_1(name) \
     502          54 :     fd_rwlock_unwrite(&bank->name##_lock);
     503           9 :   #define HAS_LOCK_0(name)
     504             : 
     505           9 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     506          45 :     HAS_COW_##cow(name);                                                   \
     507         423 :     HAS_LOCK_##has_lock(name)
     508         423 :   FD_BANKS_ITER(X)
     509           9 :   #undef X
     510           9 :   #undef HAS_COW_0
     511           9 :   #undef HAS_COW_1
     512           9 :   #undef HAS_LOCK_0
     513           9 :   #undef HAS_LOCK_1
     514             : 
     515           9 :   fd_bank_set_cost_tracker_pool( bank, fd_banks_get_cost_tracker_pool( banks ) );
     516           9 :   bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) );
     517           9 :   fd_rwlock_unwrite( &bank->cost_tracker_lock );
     518             : 
     519           9 :   bank->flags |= FD_BANK_FLAGS_INIT;
     520           9 :   bank->flags |= FD_BANK_FLAGS_FROZEN;
     521           9 :   bank->refcnt = 0UL;
     522             : 
     523           9 :   bank->first_fec_set_received_nanos      = fd_log_wallclock();
     524           9 :   bank->first_transaction_scheduled_nanos = 0L;
     525           9 :   bank->last_transaction_finished_nanos   = 0L;
     526             : 
     527             :   /* Now that the node is inserted, update the root */
     528             : 
     529           9 :   banks->root_idx = bank->idx;
     530             : 
     531           9 :   fd_rwlock_unwrite( &banks->rwlock );
     532           9 :   return bank;
     533           9 : }
     534             : 
     535             : fd_bank_t *
     536             : fd_banks_clone_from_parent( fd_banks_t * banks,
     537             :                             ulong        child_bank_idx,
     538          66 :                             ulong        parent_bank_idx ) {
     539          66 :   fd_rwlock_write( &banks->rwlock );
     540             : 
     541          66 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     542          66 :   if( FD_UNLIKELY( !bank_pool ) ) {
     543           0 :     FD_LOG_CRIT(( "invariant violation: failed to get bank pool" ));
     544           0 :   }
     545             : 
     546             :   /* Make sure that the bank is valid. */
     547             : 
     548          66 :   fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
     549          66 :   if( FD_UNLIKELY( !child_bank ) ) {
     550           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu does not exist", child_bank_idx ));
     551           0 :   }
     552          66 :   if( FD_UNLIKELY( !(child_bank->flags&FD_BANK_FLAGS_INIT) ) ) {
     553           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is not initialized", child_bank_idx ));
     554           0 :   }
     555             : 
     556             :   /* Then make sure that the parent bank is valid and frozen. */
     557             : 
     558          66 :   fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, parent_bank_idx );
     559          66 :   if( FD_UNLIKELY( !parent_bank ) ) {
     560           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu does not exist", parent_bank_idx ));
     561           0 :   }
     562          66 :   if( FD_UNLIKELY( !(parent_bank->flags&FD_BANK_FLAGS_FROZEN) ) ) {
     563           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu is not frozen", parent_bank_idx ));
     564           0 :   }
     565             : 
     566             :   /* We want to copy over the fields from the parent to the child,
     567             :      except for the fields which correspond to the header of the bank
     568             :      struct which either are used for internal memory managment or are
     569             :      fields which are not copied over from the parent bank (e.g. stake
     570             :      delegations delta and the cost tracker).  We can take advantage of
     571             :      the fact that those fields are laid out at the top of the bank
     572             :      struct. */
     573             : 
     574          66 :   fd_memcpy( &child_bank->non_cow, &parent_bank->non_cow, sizeof(child_bank->non_cow) );
     575             : 
     576          66 :   #define HAS_COW_1(type, name, footprint) \
     577         330 :     child_bank->name##_dirty    = 0;       \
     578         330 :     child_bank->name##_pool_idx = parent_bank->name##_pool_idx;
     579             : 
     580          66 :   #define HAS_COW_0(type, name, footprint)
     581             : 
     582          66 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     583         330 :     HAS_COW_##cow(type, name, footprint)
     584         330 :   FD_BANKS_ITER(X)
     585          66 :   #undef X
     586          66 :   #undef HAS_COW_0
     587          66 :   #undef HAS_COW_1
     588             : 
     589             :   /* Initialization for the non-templatized fields. */
     590             : 
     591             :   /* The cost tracker pool needs to be set for the child bank and then
     592             :      a cost tracker pool element needs to be acquired.*/
     593             : 
     594          66 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( child_bank );
     595          66 :   if( FD_UNLIKELY( fd_bank_cost_tracker_pool_free( cost_tracker_pool )==0UL ) ) {
     596           0 :     FD_LOG_CRIT(( "invariant violation: no free cost tracker pool elements" ));
     597           0 :   }
     598             : 
     599          66 :   child_bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
     600          66 :   fd_rwlock_unwrite( &child_bank->cost_tracker_lock );
     601             : 
     602          66 :   child_bank->stake_delegations_delta_dirty = 0;
     603          66 :   fd_rwlock_unwrite( &child_bank->stake_delegations_delta_lock );
     604             : 
     605             :   /* Setup locks for new bank as free. */
     606          66 :   #define HAS_LOCK_1(name) \
     607         396 :     fd_rwlock_unwrite(&child_bank->name##_lock);
     608          66 :   #define HAS_LOCK_0(name)
     609             : 
     610          66 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     611         396 :     HAS_LOCK_##has_lock(name)
     612         396 :   FD_BANKS_ITER(X)
     613          66 :   #undef X
     614          66 :   #undef HAS_LOCK_0
     615          66 :   #undef HAS_LOCK_1
     616             : 
     617             :   /* If the parent bank is dead, then we also need to mark the child
     618             :      bank as being a dead block. */
     619          66 :   if( FD_UNLIKELY( parent_bank->flags & FD_BANK_FLAGS_DEAD ) ) {
     620           0 :     child_bank->flags |= FD_BANK_FLAGS_DEAD;
     621           0 :   }
     622             : 
     623          66 :   child_bank->refcnt = 0UL;
     624             : 
     625             :   /* Now the child bank is replayable. */
     626          66 :   child_bank->flags |= FD_BANK_FLAGS_REPLAYABLE;
     627             : 
     628          66 :   fd_rwlock_unwrite( &banks->rwlock );
     629             : 
     630          66 :   return child_bank;
     631          66 : }
     632             : 
     633             : /* Apply a fd_stake_delegations_t into the root. This assumes that there
     634             :    are no in-between, un-applied banks between the root and the bank
     635             :    being applied. This also assumes that the stake delegation object
     636             :    that is being applied is a delta. */
     637             : 
     638             : static inline void
     639             : fd_banks_stake_delegations_apply_delta( fd_bank_t *              bank,
     640         105 :                                         fd_stake_delegations_t * stake_delegations_base ) {
     641             : 
     642         105 :   if( !bank->stake_delegations_delta_dirty ) {
     643          45 :     return;
     644          45 :   }
     645             : 
     646          60 :   fd_stake_delegations_t * stake_delegations_delta = fd_stake_delegations_join( bank->stake_delegations_delta );
     647          60 :   if( FD_UNLIKELY( !stake_delegations_delta ) ) {
     648           0 :     FD_LOG_CRIT(( "Failed to join stake delegations delta" ));
     649           0 :   }
     650             : 
     651          60 :   fd_stake_delegations_iter_t iter_[1];
     652          60 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations_delta );
     653         147 :        !fd_stake_delegations_iter_done( iter );
     654          87 :        fd_stake_delegations_iter_next( iter ) ) {
     655          87 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     656          87 :     if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
     657          75 :       fd_stake_delegations_update(
     658          75 :           stake_delegations_base,
     659          75 :           &stake_delegation->stake_account,
     660          75 :           &stake_delegation->vote_account,
     661          75 :           stake_delegation->stake,
     662          75 :           stake_delegation->activation_epoch,
     663          75 :           stake_delegation->deactivation_epoch,
     664          75 :           stake_delegation->credits_observed,
     665          75 :           stake_delegation->warmup_cooldown_rate
     666          75 :       );
     667          75 :     } else {
     668          12 :       fd_stake_delegations_remove( stake_delegations_base, &stake_delegation->stake_account );
     669          12 :     }
     670          87 :   }
     671          60 : }
     672             : 
     673             : /* fd_bank_stake_delegation_apply_deltas applies all of the stake
     674             :    delegations for the entire direct ancestry from the bank to the
     675             :    root into a full fd_stake_delegations_t object. */
     676             : 
     677             : static inline void
     678             : fd_bank_stake_delegation_apply_deltas( fd_banks_t *             banks,
     679             :                                        fd_bank_t *              bank,
     680          45 :                                        fd_stake_delegations_t * stake_delegations ) {
     681             : 
     682             :   /* Naively what we want to do is iterate from the old root to the new
     683             :      root and apply the delta to the full state iteratively. */
     684             : 
     685             :   /* First, gather all of the pool indicies that we want to apply deltas
     686             :      for in reverse order starting from the new root. We want to exclude
     687             :      the old root since its delta has been applied previously. */
     688          45 :   ulong pool_indicies[ banks->max_total_banks ];
     689          45 :   ulong pool_indicies_len = 0UL;
     690             : 
     691          45 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     692             : 
     693          45 :   ulong curr_idx = fd_banks_pool_idx( bank_pool, bank );
     694         150 :   while( curr_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     695         105 :     pool_indicies[pool_indicies_len++] = curr_idx;
     696         105 :     fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, curr_idx );
     697         105 :     curr_idx = curr_bank->parent_idx;
     698         105 :   }
     699             : 
     700             :   /* We have populated all of the indicies that we need to apply deltas
     701             :      from in reverse order. */
     702             : 
     703         150 :   for( ulong i=pool_indicies_len; i>0; i-- ) {
     704         105 :     ulong idx = pool_indicies[i-1UL];
     705         105 :     fd_banks_stake_delegations_apply_delta( fd_banks_pool_ele( bank_pool, idx ), stake_delegations );
     706         105 :   }
     707          45 : }
     708             : 
     709             : fd_stake_delegations_t *
     710          24 : fd_bank_stake_delegations_frontier_query( fd_banks_t * banks, fd_bank_t * bank ) {
     711             : 
     712          24 :   fd_rwlock_write( &banks->rwlock );
     713             : 
     714             :   /* First copy the rooted state into the frontier. */
     715          24 :   memcpy( banks->stake_delegations_frontier, banks->stake_delegations_root, FD_STAKE_DELEGATIONS_FOOTPRINT );
     716             : 
     717             :   /* Now apply all of the updates from the bank and all of its
     718             :      ancestors in order to the frontier. */
     719          24 :   fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( banks->stake_delegations_frontier );
     720          24 :   fd_bank_stake_delegation_apply_deltas( banks, bank, stake_delegations );
     721             : 
     722          24 :   fd_rwlock_unwrite( &banks->rwlock );
     723             : 
     724          24 :   return stake_delegations;
     725          24 : }
     726             : 
     727             : fd_stake_delegations_t *
     728           3 : fd_banks_stake_delegations_root_query( fd_banks_t * banks ) {
     729           3 :   return fd_stake_delegations_join( banks->stake_delegations_root );
     730           3 : }
     731             : 
     732             : fd_bank_t const *
     733             : fd_banks_advance_root( fd_banks_t * banks,
     734          21 :                        ulong        root_bank_idx ) {
     735             : 
     736          21 :   fd_rwlock_write( &banks->rwlock );
     737             : 
     738          21 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     739             : 
     740          21 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
     741             : 
     742             :   /* We want to replace the old root with the new root. This means we
     743             :      have to remove banks that aren't descendants of the new root. */
     744             : 
     745          21 :   fd_bank_t const * old_root = fd_banks_root( banks );
     746          21 :   if( FD_UNLIKELY( !old_root ) ) {
     747           0 :     FD_LOG_CRIT(( "invariant violation: old root is NULL" ));
     748           0 :   }
     749             : 
     750          21 :   if( FD_UNLIKELY( old_root->refcnt!=0UL ) ) {
     751           0 :     FD_LOG_CRIT(( "refcnt for old root bank at index %lu is nonzero: %lu", old_root->idx, old_root->refcnt ));
     752           0 :   }
     753             : 
     754          21 :   fd_bank_t * new_root = fd_banks_bank_query( banks, root_bank_idx );
     755          21 :   if( FD_UNLIKELY( !new_root ) ) {
     756           0 :     FD_LOG_CRIT(( "invariant violation: new root is NULL" ));
     757           0 :   }
     758             : 
     759          21 :   if( FD_UNLIKELY( new_root->parent_idx!=old_root->idx ) ) {
     760           0 :     FD_LOG_CRIT(( "invariant violation: trying to advance root bank by more than one" ));
     761           0 :   }
     762             : 
     763          21 :   fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( banks->stake_delegations_root );
     764          21 :   fd_bank_stake_delegation_apply_deltas( banks, new_root, stake_delegations );
     765          21 :   new_root->stake_delegations_delta_dirty = 0;
     766             : 
     767             :   /* Now that the deltas have been applied, we can remove all nodes
     768             :      that are not direct descendants of the new root. */
     769          21 :   fd_bank_t * head = fd_banks_pool_ele( bank_pool, old_root->idx );
     770          21 :   head->next       = fd_banks_pool_idx_null( bank_pool );
     771          21 :   fd_bank_t * tail = head;
     772             : 
     773          75 :   while( head ) {
     774          54 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, head->child_idx );
     775             : 
     776         108 :     while( FD_LIKELY( child ) ) {
     777             : 
     778          54 :       if( FD_LIKELY( child!=new_root ) ) {
     779          33 :         if( FD_UNLIKELY( child->refcnt!=0UL ) ) {
     780           0 :           FD_LOG_CRIT(( "refcnt for child bank at index %lu is %lu", child->idx, child->refcnt ));
     781           0 :         }
     782             : 
     783             :         /* Update tail pointers */
     784          33 :         tail->next = child->idx;
     785          33 :         tail       = fd_banks_pool_ele( bank_pool, tail->next );
     786          33 :         tail->next = fd_banks_pool_idx_null( bank_pool );
     787             : 
     788          33 :       }
     789             : 
     790          54 :       child = fd_banks_pool_ele( bank_pool, child->sibling_idx );
     791          54 :     }
     792             : 
     793          54 :     fd_bank_t * next = fd_banks_pool_ele( bank_pool, head->next );
     794             : 
     795             :     /* Decide if we need to free any CoW fields. We free a CoW member
     796             :        from its pool if the dirty flag is set unless it is the same
     797             :        pool that the new root uses. */
     798          54 :     #define HAS_COW_1(name)                                                          \
     799         540 :       if( head->name##_dirty && head->name##_pool_idx!=new_root->name##_pool_idx ) { \
     800           3 :         fd_rwlock_write( &banks->name##_pool_lock );                                 \
     801           3 :         fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );      \
     802           3 :         fd_bank_##name##_pool_idx_release( name##_pool, head->name##_pool_idx );     \
     803           3 :         fd_rwlock_unwrite( &banks->name##_pool_lock );                               \
     804           3 :       }
     805             :     /* Do nothing for these. */
     806          54 :     #define HAS_COW_0(name)
     807             : 
     808          54 :     #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     809         270 :       HAS_COW_##cow(name)
     810         270 :     FD_BANKS_ITER(X)
     811          54 :     #undef X
     812          54 :     #undef HAS_COW_0
     813          54 :     #undef HAS_COW_1
     814             : 
     815          54 :     head->flags = 0UL;
     816          54 :     fd_banks_pool_ele_release( bank_pool, head );
     817          54 :     head = next;
     818          54 :   }
     819             : 
     820             :   /* If the new root did not have the dirty bit set, that means the node
     821             :      didn't own the pool index. Change the ownership to the new root. */
     822          21 :   #define HAS_COW_1(name)                                                            \
     823         105 :     fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks );          \
     824         105 :     if( new_root->name##_pool_idx!=fd_bank_##name##_pool_idx_null( name##_pool ) ) { \
     825           9 :       new_root->name##_dirty = 1;                                                    \
     826           9 :     }
     827             :   /* Do nothing if not CoW. */
     828          21 :   #define HAS_COW_0(name)
     829             : 
     830          21 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     831         105 :     HAS_COW_##cow(name)
     832         105 :   FD_BANKS_ITER(X)
     833          21 :   #undef X
     834          21 :   #undef HAS_COW_0
     835          21 :   #undef HAS_COW_1
     836             : 
     837          21 :   new_root->parent_idx = null_idx;
     838          21 :   banks->root_idx      = new_root->idx;
     839             : 
     840          21 :   fd_rwlock_unwrite( &banks->rwlock );
     841             : 
     842          21 :   return new_root;
     843          21 : }
     844             : 
     845             : void
     846           3 : fd_banks_clear_bank( fd_banks_t * banks, fd_bank_t * bank ) {
     847             : 
     848           3 :   fd_rwlock_read( &banks->rwlock );
     849             :   /* Get the parent bank. */
     850           3 :   fd_bank_t * parent_bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank->parent_idx );
     851             : 
     852           3 :   fd_memset( &bank->non_cow, 0, sizeof(bank->non_cow) );
     853             : 
     854           3 :   #define HAS_COW_1(type, name, footprint)                                                                                  \
     855          15 :     fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank );                                                   \
     856          15 :     if( bank->name##_dirty ) {                                                                                              \
     857             :       /* If the dirty flag is set, then we have a pool allocated for */                                                     \
     858             :       /* this specific bank. We need to release the pool index and   */                                                     \
     859             :       /* assign the bank to the idx corresponding to the parent.     */                                                     \
     860           6 :       fd_bank_##name##_pool_idx_release( name##_pool, bank->name##_pool_idx );                                              \
     861           6 :       bank->name##_dirty    = 0;                                                                                            \
     862           6 :       bank->name##_pool_idx = parent_bank ? parent_bank->name##_pool_idx : fd_bank_##name##_pool_idx_null( name##_pool );   \
     863           6 :     }
     864             : 
     865           3 :   #define HAS_COW_0(type, name, footprint)
     866             : 
     867           3 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
     868          15 :     HAS_COW_##cow(type, name, footprint)
     869          15 :   FD_BANKS_ITER(X)
     870           3 :   #undef X
     871           3 :   #undef HAS_COW_0
     872           3 :   #undef HAS_COW_1
     873             : 
     874             :   /* We need to acquire a cost tracker element. */
     875           3 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank );
     876           3 :   if( FD_UNLIKELY( bank->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) ) {
     877           3 :     fd_bank_cost_tracker_pool_idx_release( cost_tracker_pool, bank->cost_tracker_pool_idx );
     878           3 :   }
     879           3 :   bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
     880           3 :   fd_rwlock_unwrite( &bank->cost_tracker_lock );
     881             : 
     882           3 :   bank->stake_delegations_delta_dirty = 0;
     883           3 :   fd_rwlock_unwrite( &bank->stake_delegations_delta_lock );
     884             : 
     885           3 :   fd_rwlock_unread( &banks->rwlock );
     886           3 : }
     887             : 
     888             : /* Is the fork tree starting at the given bank entirely eligible for
     889             :    pruning?  Returns 1 for yes, 0 for no.
     890             : 
     891             :    See comment in fd_replay_tile.c for more details on safe pruning. */
     892             : static int
     893          27 : fd_banks_subtree_can_be_pruned( fd_bank_t * bank_pool, fd_bank_t * bank ) {
     894          27 :   if( FD_UNLIKELY( !bank ) ) {
     895           0 :     FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
     896           0 :   }
     897             : 
     898          27 :   if( bank->refcnt!=0UL ) {
     899           3 :     return 0;
     900           3 :   }
     901             : 
     902             :   /* Recursively check all children. */
     903          24 :   ulong child_idx = bank->child_idx;
     904          33 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     905           9 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, child_idx );
     906           9 :     if( !fd_banks_subtree_can_be_pruned( bank_pool, child ) ) {
     907           0 :       return 0;
     908           0 :     }
     909           9 :     child_idx = child->sibling_idx;
     910           9 :   }
     911             : 
     912          24 :   return 1;
     913          24 : }
     914             : 
     915             : /* Mark everything in the fork tree starting at the given bank dead. */
     916             : 
     917             : static void
     918          27 : fd_banks_subtree_mark_dead( fd_bank_t * bank_pool, fd_bank_t * bank ) {
     919          27 :   if( FD_UNLIKELY( !bank ) ) {
     920           0 :     FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
     921           0 :   }
     922          27 :   if( FD_UNLIKELY( bank->flags & FD_BANK_FLAGS_ROOTED ) ) {
     923           0 :     FD_LOG_CRIT(( "invariant violation: bank for idx %lu is rooted", bank->idx ));
     924           0 :   }
     925             : 
     926          27 :   bank->flags |= FD_BANK_FLAGS_DEAD;
     927             : 
     928             :   /* Recursively mark all children as dead. */
     929          27 :   ulong child_idx = bank->child_idx;
     930          36 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     931           9 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, child_idx );
     932           9 :     fd_banks_subtree_mark_dead( bank_pool, child );
     933           9 :     child_idx = child->sibling_idx;
     934           9 :   }
     935          27 : }
     936             : 
     937             : int
     938             : fd_banks_advance_root_prepare( fd_banks_t * banks,
     939             :                                ulong        target_bank_idx,
     940          15 :                                ulong *      advanceable_bank_idx_out ) {
     941             :   /* TODO: An optimization here is to do a single traversal of the tree
     942             :      that would mark minority forks as dead while accumulating
     943             :      refcnts to determine which bank is the highest advanceable. */
     944             : 
     945          15 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     946          15 :   fd_rwlock_read( &banks->rwlock );
     947             : 
     948          15 :   fd_bank_t * root = fd_banks_root( banks );
     949          15 :   if( FD_UNLIKELY( !root ) ) {
     950           0 :     FD_LOG_WARNING(( "failed to get root bank" ));
     951           0 :     fd_rwlock_unread( &banks->rwlock );
     952           0 :     return 0;
     953           0 :   }
     954             : 
     955             :   /* Early exit if target is the same as the old root. */
     956          15 :   if( FD_UNLIKELY( root->idx==target_bank_idx ) ) {
     957           0 :     FD_LOG_WARNING(( "target bank_idx %lu is the same as the old root's bank index %lu", target_bank_idx, root->idx ));
     958           0 :     fd_rwlock_unread( &banks->rwlock );
     959           0 :     return 0;
     960           0 :   }
     961             : 
     962             :   /* Early exit if the root bank still has a reference to it, we can't
     963             :      advance from it unti it's released. */
     964          15 :   if( FD_UNLIKELY( root->refcnt!=0UL ) ) {
     965           0 :     fd_rwlock_unread( &banks->rwlock );
     966           0 :     return 0;
     967           0 :   }
     968             : 
     969          15 :   fd_bank_t * target_bank = fd_banks_pool_ele( bank_pool, target_bank_idx );
     970          15 :   if( FD_UNLIKELY( !target_bank ) ) {
     971           0 :     FD_LOG_CRIT(( "failed to get bank for valid pool idx %lu", target_bank_idx ));
     972           0 :   }
     973             : 
     974             :   /* Mark every node from the target bank up through its parents to the
     975             :      root as being rooted.  We also need to figure out the oldest,
     976             :      non-rooted ancestor of the target bank since we only want to
     977             :      advance our root bank by one. */
     978          15 :   fd_bank_t * curr = target_bank;
     979          15 :   fd_bank_t * prev = NULL;
     980          57 :   while( curr && curr!=root ) {
     981          42 :     curr->flags |= FD_BANK_FLAGS_ROOTED;
     982          42 :     prev         = curr;
     983          42 :     curr         = fd_banks_pool_ele( bank_pool, curr->parent_idx );
     984          42 :   }
     985             : 
     986          15 :   ulong advance_candidate_idx = prev->idx;
     987             : 
     988             :   /* If we didn't reach the old root or there is no parent, target is
     989             :      not a descendant. */
     990          15 :   if( FD_UNLIKELY( !curr || prev->parent_idx!=root->idx ) ) {
     991           0 :     FD_LOG_CRIT(( "invariant violation: target bank_idx %lu is not a direct descendant of root bank_idx %lu %lu %lu", target_bank_idx, root->idx, prev->idx, prev->parent_idx ));
     992           0 :   }
     993             : 
     994             :   /* We should mark the old root bank as dead. */
     995          15 :   root->flags |= FD_BANK_FLAGS_DEAD;
     996             : 
     997             :   /* We will at most advance our root bank by one.  This means we can
     998             :      advance our root bank by one if each of the siblings of the
     999             :      potential new root are eligible for pruning.  Each of the sibling
    1000             :      subtrees can be pruned if the subtrees have no active references on
    1001             :      their bank. */
    1002          15 :   ulong child_idx = root->child_idx;
    1003          42 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
    1004          30 :     fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_idx );
    1005          30 :     if( child_idx!=advance_candidate_idx ) {
    1006          18 :       fd_banks_subtree_mark_dead( bank_pool, child_bank );
    1007          18 :       if( !fd_banks_subtree_can_be_pruned( bank_pool, child_bank ) ) {
    1008           3 :         fd_rwlock_unread( &banks->rwlock );
    1009           3 :         return 0;
    1010           3 :       }
    1011          18 :     }
    1012          27 :     child_idx = child_bank->sibling_idx;
    1013          27 :   }
    1014             : 
    1015          12 :   *advanceable_bank_idx_out = advance_candidate_idx;
    1016          12 :   fd_rwlock_unread( &banks->rwlock );
    1017          12 :   return 1;
    1018          15 : }
    1019             : 
    1020             : fd_bank_t *
    1021             : fd_banks_new_bank( fd_banks_t * banks,
    1022             :                    ulong        parent_bank_idx,
    1023          66 :                    long         now ) {
    1024             : 
    1025          66 :   fd_rwlock_write( &banks->rwlock );
    1026             : 
    1027          66 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
    1028          66 :   if( FD_UNLIKELY( !bank_pool ) ) {
    1029           0 :     FD_LOG_CRIT(( "invariant violation: failed to get bank pool" ));
    1030           0 :   }
    1031             : 
    1032          66 :   if( FD_UNLIKELY( fd_banks_pool_free( bank_pool )==0UL ) ) {
    1033           0 :     FD_LOG_CRIT(( "invariant violation: no free bank indices available" ));
    1034           0 :   }
    1035             : 
    1036          66 :   ulong child_bank_idx = fd_banks_pool_idx_acquire( bank_pool );
    1037             : 
    1038             :   /* Make sure that the bank is valid. */
    1039             : 
    1040          66 :   fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
    1041          66 :   if( FD_UNLIKELY( !child_bank ) ) {
    1042           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu does not exist", child_bank_idx ));
    1043           0 :   }
    1044          66 :   if( FD_UNLIKELY( child_bank->flags&FD_BANK_FLAGS_INIT ) ) {
    1045           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is already initialized", child_bank_idx ));
    1046           0 :   }
    1047             : 
    1048          66 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
    1049             : 
    1050          66 :   child_bank->idx         = child_bank_idx;
    1051          66 :   child_bank->parent_idx  = null_idx;
    1052          66 :   child_bank->child_idx   = null_idx;
    1053          66 :   child_bank->sibling_idx = null_idx;
    1054          66 :   child_bank->next        = null_idx;
    1055          66 :   child_bank->flags       = FD_BANK_FLAGS_INIT;
    1056             : 
    1057             :   /* Then make sure that the parent bank is valid and frozen. */
    1058             : 
    1059          66 :   fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, parent_bank_idx );
    1060          66 :   if( FD_UNLIKELY( !parent_bank ) ) {
    1061           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu does not exist", parent_bank_idx ));
    1062           0 :   }
    1063          66 :   if( FD_UNLIKELY( !(parent_bank->flags&FD_BANK_FLAGS_INIT) ) ) {
    1064           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is already initialized", child_bank_idx ));
    1065           0 :   }
    1066             : 
    1067             :   /* Link node->parent */
    1068             : 
    1069          66 :   child_bank->parent_idx = parent_bank_idx;
    1070             : 
    1071             :   /* Link parent->node and sibling->node */
    1072             : 
    1073          66 :   if( FD_LIKELY( parent_bank->child_idx==null_idx ) ) {
    1074             : 
    1075             :     /* This is the first child so set as left-most child */
    1076             : 
    1077          39 :     parent_bank->child_idx = child_bank_idx;
    1078             : 
    1079          39 :   } else {
    1080             :     /* Already have children so iterate to right-most sibling. */
    1081             : 
    1082          27 :     fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, parent_bank->child_idx );
    1083          27 :     if( FD_UNLIKELY( !curr_bank ) ) {
    1084           0 :       FD_LOG_CRIT(( "Invariant violation: child bank for bank index %lu does not exist", parent_bank->child_idx ));
    1085           0 :     }
    1086          33 :     while( curr_bank->sibling_idx != null_idx ) curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->sibling_idx );
    1087             : 
    1088             :     /* Link to right-most sibling. */
    1089             : 
    1090          27 :     curr_bank->sibling_idx = child_bank_idx;
    1091          27 :   }
    1092             : 
    1093          66 :   child_bank->first_fec_set_received_nanos = now;
    1094          66 :   child_bank->first_transaction_scheduled_nanos = 0L;
    1095          66 :   child_bank->last_transaction_finished_nanos = 0L;
    1096             : 
    1097          66 :   fd_rwlock_unwrite( &banks->rwlock );
    1098          66 :   return child_bank;
    1099          66 : }
    1100             : 
    1101             : void
    1102             : fd_banks_mark_bank_dead( fd_banks_t * banks,
    1103           0 :                          fd_bank_t *  bank ) {
    1104           0 :   fd_rwlock_write( &banks->rwlock );
    1105             : 
    1106           0 :   fd_banks_subtree_mark_dead( fd_banks_get_bank_pool( banks ), bank );
    1107             : 
    1108           0 :   fd_rwlock_unwrite( &banks->rwlock );
    1109           0 : }
    1110             : 
    1111             : void
    1112             : fd_banks_mark_bank_frozen( fd_banks_t * banks,
    1113          63 :                            fd_bank_t *  bank ) {
    1114          63 :   if( FD_UNLIKELY( bank->flags&FD_BANK_FLAGS_FROZEN ) ) {
    1115           0 :     FD_LOG_CRIT(( "invariant violation: bank for idx %lu is already frozen", bank->idx ));
    1116           0 :   }
    1117             : 
    1118          63 :   fd_rwlock_write( &banks->rwlock );
    1119          63 :   bank->flags |= FD_BANK_FLAGS_FROZEN;
    1120             : 
    1121          63 :   if( FD_UNLIKELY( bank->cost_tracker_pool_idx==fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) ) ) ) {
    1122           0 :     FD_LOG_CRIT(( "invariant violation: cost tracker pool index is null" ));
    1123           0 :   }
    1124          63 :   fd_bank_cost_tracker_pool_idx_release( fd_bank_get_cost_tracker_pool( bank ), bank->cost_tracker_pool_idx );
    1125          63 :   bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) );
    1126          63 :   fd_rwlock_unwrite( &banks->rwlock );
    1127          63 : }
    1128             : 
    1129             : int
    1130           0 : fd_banks_validate( fd_banks_t * banks ) {
    1131           0 :   fd_rwlock_read( &banks->rwlock );
    1132             : 
    1133           0 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
    1134             : 
    1135           0 :   FD_LOG_INFO(( "fd_banks_pool_free: %lu", fd_banks_pool_free( bank_pool ) ));
    1136             : 
    1137             :   /* First check that the number of elements acquired by the CoW pools
    1138             :      is not greater than the number of elements in the bank pool. */
    1139           0 :   #define HAS_COW_1(type, name, footprint)                                                                                                                                                      \
    1140           0 :   fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank );                                                                                                                         \
    1141           0 :   if( fd_bank_##name##_pool_used( name##_pool ) > fd_bank_pool_used( bank_pool ) ) {                                                                                                            \
    1142           0 :     FD_LOG_WARNING(( "Invariant violation: %s pool has more elements acquired than the bank pool %lu %lu", #name, fd_bank_##name##_pool_used( name##_pool ), fd_bank_pool_used( bank_pool ) )); \
    1143           0 :     fd_rwlock_unread( &banks->rwlock );                                                                                                                                                         \
    1144           0 :     return 1;                                                                                                                                                                                   \
    1145           0 :   }                                                                                                                                                                                             \
    1146           0 : 
    1147           0 :   #define HAS_COW_0(type, name, footprint)
    1148             : 
    1149           0 :   #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
    1150           0 :     HAS_COW_##cow(type, name, footprint)                                   \
    1151           0 :   FD_BANKS_ITER(X)
    1152           0 :   #undef X
    1153           0 :   #undef HAS_COW_0
    1154           0 :   #undef HAS_COW_1
    1155           0 :   fd_rwlock_unread( &banks->rwlock );
    1156             : 
    1157           0 :   return 0;
    1158           0 : }

Generated by: LCOV version 1.14