LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 563 769 73.2 %
Date: 2025-10-27 04:40:00 Functions: 45 213 21.1 %

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

Generated by: LCOV version 1.14