LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_impl_v1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 202 253 79.8 %
Date: 2026-03-19 18:19:27 Functions: 13 14 92.9 %

          Line data    Source code
       1             : #include "fd_accdb_impl_v1.h"
       2             : #include "fd_accdb_lineage.h"
       3             : #include "fd_accdb_funk.h"
       4             : #include <stdatomic.h>
       5             : 
       6             : FD_STATIC_ASSERT( alignof(fd_accdb_user_v1_t)<=alignof(fd_accdb_user_t), layout );
       7             : FD_STATIC_ASSERT( sizeof (fd_accdb_user_v1_t)<=sizeof(fd_accdb_user_t),  layout );
       8             : 
       9             : static int
      10             : fd_accdb_search_chain( fd_funk_t const *          funk,
      11             :                        fd_accdb_lineage_t const * lineage,
      12             :                        ulong                      chain_idx,
      13             :                        fd_funk_rec_key_t const *  key,
      14      350469 :                        fd_funk_rec_t **           out_rec ) {
      15      350469 :   *out_rec = NULL;
      16             : 
      17      350469 :   fd_funk_rec_map_shmem_t const *               shmap     = funk->rec_map->map;
      18      350469 :   fd_funk_rec_map_shmem_private_chain_t const * chain_tbl = fd_funk_rec_map_shmem_private_chain_const( shmap, 0UL );
      19      350469 :   fd_funk_rec_map_shmem_private_chain_t const * chain     = chain_tbl + chain_idx;
      20      350469 :   fd_funk_rec_t *                               rec_tbl   = funk->rec_pool->ele;
      21      350469 :   ulong                                         rec_max   = fd_funk_rec_pool_ele_max( funk->rec_pool );
      22             : 
      23      350469 :   uint            ele_idx   = chain->head_cidx;
      24      350469 :   ulong _Atomic * ver_cnt_p = (ulong _Atomic *)&chain->ver_cnt;
      25      350469 :   ulong           ver_cnt   = atomic_load_explicit( ver_cnt_p, memory_order_acquire );
      26             : 
      27             :   /* Start a speculative transaction for the chain containing revisions
      28             :      of the account key we are looking for. */
      29      350469 :   ulong cnt = fd_funk_rec_map_private_vcnt_cnt( ver_cnt );
      30      350469 :   if( FD_UNLIKELY( fd_funk_rec_map_private_vcnt_ver( ver_cnt )&1 ) ) {
      31           0 :     return FD_MAP_ERR_AGAIN; /* chain is locked */
      32           0 :   }
      33             : 
      34             :   /* Walk the map chain, bail at the first entry
      35             :      (Each chain is sorted newest-to-oldest) */
      36      350469 :   fd_funk_rec_t * best = NULL;
      37      364029 :   for( ulong i=0UL; i<cnt; i++ ) {
      38      100161 :     uint ele_next = rec_tbl[ ele_idx ].map_next;
      39      100161 :     if( FD_UNLIKELY( atomic_load_explicit( ver_cnt_p, memory_order_acquire )!=ver_cnt ) ) {
      40           0 :       return FD_MAP_ERR_AGAIN;
      41           0 :     }
      42             : 
      43      100161 :     if( FD_UNLIKELY( ele_idx>=rec_max ) ) {
      44           0 :       FD_LOG_CRIT(( "fd_accdb_search_chain detected memory corruption: invalid ele_idx at node %lu:%u (rec_max %lu)",
      45           0 :                     chain_idx, ele_idx, rec_max ));
      46           0 :     }
      47             : 
      48      100161 :     fd_funk_rec_t * rec = &rec_tbl[ ele_idx ];
      49             : 
      50             :     /* Skip over unrelated records (hash collision) */
      51      100161 :     if( FD_UNLIKELY( !fd_funk_rec_key_eq( rec->pair.key, key ) ) ) goto next;
      52             : 
      53             :     /* Confirm that record is part of the current fork */
      54       86603 :     if( FD_UNLIKELY( !fd_accdb_lineage_has_xid( lineage, rec->pair.xid ) ) ) goto next;
      55             : 
      56       86603 :     if( FD_UNLIKELY( ele_next==ele_idx ) ) {
      57           0 :       FD_LOG_CRIT(( "fd_accdb_search_chain detected cycle" ));
      58           0 :     }
      59       86603 :     best = rec;
      60       86603 :     break;
      61             : 
      62       13560 : next:
      63       13560 :     ele_idx = ele_next;
      64       13560 :   }
      65      350471 :   if( FD_UNLIKELY( !best && ele_idx!=FD_FUNK_REC_IDX_NULL ) ) {
      66           0 :     FD_LOG_CRIT(( "fd_accdb_search_chain detected malformed chain (%lu): found more nodes than chain header indicated (%lu)", chain_idx, cnt ));
      67           0 :   }
      68             : 
      69      350471 :   if( FD_UNLIKELY( atomic_load_explicit( ver_cnt_p, memory_order_acquire )!=ver_cnt ) ) {
      70           0 :     return FD_MAP_ERR_AGAIN;
      71           0 :   }
      72      350471 :   *out_rec = best;
      73      350471 :   return FD_MAP_SUCCESS;
      74      350471 : }
      75             : 
      76             : fd_accdb_ro_t *
      77             : fd_accdb_peek_funk( fd_accdb_user_v1_t *      accdb,
      78             :                     fd_accdb_ro_t *           ro,
      79             :                     fd_funk_txn_xid_t const * xid,
      80      351546 :                     void const *              address ) {
      81      351546 :   fd_funk_t const * funk = accdb->funk;
      82      351546 :   fd_funk_rec_key_t key[1]; memcpy( key->uc, address, 32UL );
      83             : 
      84             :   /* Hash key to chain */
      85      351546 :   fd_funk_xid_key_pair_t pair[1];
      86      351546 :   fd_funk_txn_xid_copy( pair->xid, xid );
      87      351546 :   fd_funk_rec_key_copy( pair->key, key );
      88      351546 :   fd_funk_rec_map_t const * rec_map = funk->rec_map;
      89      351546 :   ulong hash      = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
      90      351546 :   ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
      91             : 
      92             :   /* Traverse chain for candidate */
      93      351546 :   fd_funk_rec_t * rec = NULL;
      94      351546 :   for(;;) {
      95      350434 :     int err = fd_accdb_search_chain( accdb->funk, accdb->lineage, chain_idx, key, &rec );
      96      350434 :     if( FD_LIKELY( err==FD_MAP_SUCCESS ) ) break;
      97         258 :     FD_SPIN_PAUSE();
      98             :     /* FIXME backoff */
      99         258 :   }
     100      351546 :   if( !rec ) return NULL;
     101             : 
     102       86675 :   memcpy( ro->ref->address, address, 32UL );
     103       86675 :   ro->ref->accdb_type = FD_ACCDB_TYPE_V1;
     104       86675 :   ro->ref->ref_type   = FD_ACCDB_REF_RO;
     105       86675 :   ro->ref->user_data  = (ulong)rec;
     106       86675 :   ro->ref->user_data2 = 0UL;
     107       86675 :   ro->meta            = fd_funk_val( rec, funk->wksp );
     108       86675 :   return ro;
     109      351546 : }
     110             : 
     111             : static ulong
     112           0 : fd_accdb_user_v1_batch_max( fd_accdb_user_t * accdb ) {
     113           0 :   (void)accdb;
     114           0 :   return ULONG_MAX;
     115           0 : }
     116             : 
     117             : void
     118          12 : fd_accdb_user_v1_fini( fd_accdb_user_t * accdb ) {
     119          12 :   fd_accdb_user_v1_t * user = (fd_accdb_user_v1_t *)accdb;
     120             : 
     121          12 :   if( FD_UNLIKELY( !fd_funk_leave( user->funk, NULL, NULL ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
     122          12 : }
     123             : 
     124             : void
     125             : fd_accdb_user_v1_open_ro_multi( fd_accdb_user_t *         accdb,
     126             :                                 fd_accdb_ro_t *           ro,
     127             :                                 fd_funk_txn_xid_t const * xid,
     128             :                                 void const *              address,
     129      227082 :                                 ulong                     cnt ) {
     130      227082 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     131      227082 :   fd_accdb_lineage_set_fork( v1->lineage, v1->funk, xid );
     132      227082 :   ulong addr_laddr = (ulong)address;
     133      454086 :   for( ulong i=0UL; i<cnt; i++ ) {
     134      227004 :     void const *    addr_i = (void const *)( (ulong)addr_laddr + i*32UL );
     135      227004 :     if( !fd_accdb_peek_funk( v1, &ro[i], xid, addr_i ) ) {
     136      141931 :       fd_accdb_ro_init_empty( &ro[i], addr_i );
     137      141931 :     } else {
     138       85073 :       v1->base.ro_active++;
     139       85073 :     }
     140      227004 :   }
     141      227082 : }
     142             : 
     143             : static void
     144             : fd_accdb_user_v1_close_ro( fd_accdb_user_t * accdb,
     145       84500 :                            fd_accdb_ro_t *   ro ) {
     146       84500 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     147             : 
     148       84500 :   v1->base.ro_active--;
     149       84500 :   (void)ro;
     150       84500 : }
     151             : 
     152             : fd_accdb_rw_t *
     153             : fd_accdb_user_v1_open_rw( fd_accdb_user_t *         accdb,
     154             :                           fd_accdb_rw_t *           rw,
     155             :                           fd_funk_txn_xid_t const * xid,
     156             :                           void const *              address,
     157             :                           ulong                     data_max,
     158      128891 :                           int                       flags ) {
     159      128891 :   fd_accdb_user_v1_t * v1  = (fd_accdb_user_v1_t *)accdb;
     160             : 
     161      128891 :   int const flag_create    = !!( flags & FD_ACCDB_FLAG_CREATE   );
     162      128891 :   int const flag_truncate  = !!( flags & FD_ACCDB_FLAG_TRUNCATE );
     163      128891 :   if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_CREATE|FD_ACCDB_FLAG_TRUNCATE) ) ) {
     164           0 :     FD_LOG_CRIT(( "invalid flags for open_rw: %#02x", (uint)flags ));
     165           0 :   }
     166             : 
     167             :   /* Pivot to different fork */
     168      128891 :   fd_accdb_lineage_set_fork( v1->lineage, v1->funk, xid );
     169      128891 :   fd_funk_txn_t * txn = fd_accdb_lineage_write_check( v1->lineage, v1->funk );
     170             : 
     171             :   /* Query old record value */
     172             : 
     173      128891 :   fd_accdb_ro_t ro[1];
     174      128891 :   if( FD_UNLIKELY( !fd_accdb_peek_funk( v1, ro, xid, address ) ) ) {
     175             :     /* Record not found */
     176      125803 :     if( flag_create ) return fd_accdb_funk_create( v1->funk, rw, txn, address, data_max );
     177         286 :     return NULL;
     178      125803 :   }
     179             : 
     180        3088 :   if( !ro->meta->lamports ) {
     181             :     /* Record previously deleted */
     182           0 :     if( !flag_create ) return NULL;
     183           0 :   }
     184             : 
     185        3088 :   fd_funk_rec_t * rec = (fd_funk_rec_t *)ro->ref->user_data;
     186        3088 :   if( fd_funk_txn_xid_eq( rec->pair.xid, xid ) ) {
     187             : 
     188             :     /* Mutable record found, modify in-place */
     189         303 :     ulong  acc_orig_sz = fd_accdb_ref_data_sz( ro );
     190         303 :     ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
     191         303 :     void * val         = fd_funk_val_truncate( rec, v1->funk->alloc, v1->funk->wksp, 16UL, val_sz_min, NULL );
     192         303 :     if( FD_UNLIKELY( !val ) ) {
     193           0 :       FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     194           0 :     }
     195         303 :     fd_accdb_funk_prep_inplace( rw, v1->funk, rec );
     196         303 :     if( flag_truncate ) {
     197           2 :       rec->val_sz = sizeof(fd_account_meta_t);
     198           2 :       rw->meta->dlen = 0;
     199           2 :     }
     200         303 :     return rw;
     201             : 
     202        2785 :   } else {
     203             : 
     204             :     /* Frozen record found, copy out to new object */
     205        2785 :     ulong  acc_orig_sz = fd_accdb_ref_data_sz( ro );
     206        2785 :     ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
     207        2785 :     ulong  val_sz      = flag_truncate ? sizeof(fd_account_meta_t) : rec->val_sz;
     208        2785 :     ulong  val_max     = 0UL;
     209        2785 :     void * val         = fd_alloc_malloc_at_least( v1->funk->alloc, 16UL, val_sz_min, &val_max );
     210        2785 :     if( FD_UNLIKELY( !val ) ) {
     211           0 :       FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     212           0 :     }
     213             : 
     214        2785 :     fd_account_meta_t * meta            = val;
     215        2785 :     uchar *             data            = (uchar *)( meta+1 );
     216        2785 :     ulong               data_max_actual = val_max - sizeof(fd_account_meta_t);
     217        2785 :     if( flag_truncate ) fd_accdb_funk_copy_truncated( meta,       ro->meta );
     218        2189 :     else                fd_accdb_funk_copy_account  ( meta, data, ro->meta, fd_account_data( ro->meta ) );
     219        2785 :     if( acc_orig_sz<data_max_actual ) {
     220             :       /* Zero out trailing data */
     221        1800 :       uchar * tail    = data           +acc_orig_sz;
     222        1800 :       ulong   tail_sz = data_max_actual-acc_orig_sz;
     223        1800 :       fd_memset( tail, 0, tail_sz );
     224        1800 :     }
     225             : 
     226        2785 :     accdb->base.created_cnt++;
     227        2785 :     return fd_accdb_funk_prep_create( rw, v1->funk, txn, address, val, val_sz, val_max );
     228             : 
     229        2785 :   }
     230        3088 : }
     231             : 
     232             : void
     233             : fd_accdb_user_v1_open_rw_multi( fd_accdb_user_t *         accdb,
     234             :                                 fd_accdb_rw_t *           rw,
     235             :                                 fd_funk_txn_xid_t const * xid,
     236             :                                 void const *              address,
     237             :                                 ulong const *             data_max,
     238             :                                 int                       flags,
     239      128978 :                                 ulong                     cnt ) {
     240      128978 :   ulong addr_laddr = (ulong)address;
     241      257988 :   for( ulong i=0UL; i<cnt; i++ ) {
     242      129010 :     void const *    addr_i = (void const *)( (ulong)addr_laddr + i*32UL );
     243      129010 :     ulong           dmax_i = data_max[i];
     244      129010 :     fd_accdb_rw_t * rw_i   = fd_accdb_user_v1_open_rw( accdb, &rw[i], xid, addr_i, dmax_i, flags );
     245      129010 :     if( !rw_i ) memset( &rw[i], 0, sizeof(fd_accdb_rw_t) );
     246      128711 :     else        accdb->base.rw_active++;
     247      129010 :   }
     248      128978 : }
     249             : 
     250             : void
     251             : fd_accdb_user_v1_close_rw( fd_accdb_user_t * accdb,
     252      128625 :                            fd_accdb_rw_t *   write ) {
     253      128625 :   if( FD_UNLIKELY( !accdb ) ) FD_LOG_CRIT(( "NULL accdb" ));
     254      128625 :   fd_accdb_user_v1_t * v1  = (fd_accdb_user_v1_t *)accdb;
     255      128625 :   fd_funk_rec_t *      rec = (fd_funk_rec_t *)write->ref->user_data;
     256             : 
     257      128625 :   if( FD_UNLIKELY( write->ref->accdb_type!=FD_ACCDB_TYPE_V1 ) ) {
     258           0 :     FD_LOG_CRIT(( "invalid accdb_type %u in fd_accdb_user_v1_close_rw", (uint)write->ref->accdb_type ));
     259           0 :   }
     260             : 
     261      128625 :   if( FD_UNLIKELY( !v1->base.rw_active ) ) {
     262           0 :     FD_LOG_CRIT(( "Failed to modify account: ref count underflow" ));
     263           0 :   }
     264             : 
     265      128625 :   if( write->ref->user_data2 ) {
     266      128283 :     fd_funk_txn_t * txn = (fd_funk_txn_t *)write->ref->user_data2;
     267      128283 :     fd_funk_rec_prepare_t prepare = {
     268      128283 :       .rec          = rec,
     269      128283 :       .rec_head_idx = &txn->rec_head_idx,
     270      128283 :       .rec_tail_idx = &txn->rec_tail_idx
     271      128283 :     };
     272      128283 :     fd_funk_rec_publish( v1->funk, &prepare );
     273      128283 :   }
     274             : 
     275      128625 :   memset( write, 0, sizeof(fd_accdb_rw_t) );
     276      128625 :   v1->base.rw_active--;
     277      128625 : }
     278             : 
     279             : void
     280             : fd_accdb_user_v1_close_ref_multi( fd_accdb_user_t * accdb,
     281             :                                   fd_accdb_ref_t *  ref0,
     282      212616 :                                   ulong             cnt ) {
     283      426867 :   for( ulong i=0UL; i<cnt; i++ ) {
     284      212399 :     if( ref0[ i ].accdb_type==FD_ACCDB_TYPE_NONE ) continue;
     285      212399 :     switch( ref0[ i ].ref_type ) {
     286       84499 :     case FD_ACCDB_REF_RO:
     287       84499 :       fd_accdb_user_v1_close_ro( accdb, (fd_accdb_ro_t *)ref0+i );
     288       84499 :       break;
     289      128618 :     case FD_ACCDB_REF_RW:
     290      128618 :       fd_accdb_user_v1_close_rw( accdb, (fd_accdb_rw_t *)ref0+i );
     291      128618 :       break;
     292           0 :     default:
     293           0 :       FD_LOG_CRIT(( "invalid ref_type %u in fd_accdb_user_v1_close_ref", (uint)ref0[ i ].ref_type ));
     294      212399 :     }
     295      212399 :   }
     296      212616 : }
     297             : 
     298             : ulong
     299             : fd_accdb_user_v1_rw_data_max( fd_accdb_user_t *     accdb,
     300      101448 :                               fd_accdb_rw_t const * rw ) {
     301      101448 :   (void)accdb;
     302      101448 :   if( rw->ref->accdb_type==FD_ACCDB_TYPE_NONE ) {
     303           0 :     return rw->ref->user_data; /* data_max */
     304           0 :   }
     305      101448 :   fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
     306      101448 :   return (ulong)( rec->val_max - sizeof(fd_account_meta_t) );
     307      101448 : }
     308             : 
     309             : void
     310             : fd_accdb_user_v1_rw_data_sz_set( fd_accdb_user_t * accdb,
     311             :                                  fd_accdb_rw_t *   rw,
     312             :                                  ulong             data_sz,
     313      102936 :                                  int               flags ) {
     314      102936 :   int flag_dontzero = !!( flags & FD_ACCDB_FLAG_DONTZERO );
     315      102936 :   if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_DONTZERO) ) ) {
     316           0 :     FD_LOG_CRIT(( "invalid flags for rw_data_sz_set: %#02x", (uint)flags ));
     317           0 :   }
     318             : 
     319      102936 :   ulong prev_sz = rw->meta->dlen;
     320      102936 :   if( data_sz>prev_sz ) {
     321      101445 :     ulong data_max = fd_accdb_user_v1_rw_data_max( accdb, rw );
     322      101445 :     if( FD_UNLIKELY( data_sz>data_max ) ) {
     323           0 :       FD_LOG_CRIT(( "attempted to write %lu bytes into a rec with only %lu bytes of data space",
     324           0 :                     data_sz, data_max ));
     325           0 :     }
     326      101445 :     if( !flag_dontzero ) {
     327           0 :       void * tail = (uchar *)fd_accdb_ref_data( rw ) + prev_sz;
     328           0 :       fd_memset( tail, 0, data_sz-prev_sz );
     329           0 :     }
     330      101445 :   }
     331      102936 :   rw->meta->dlen = (uint)data_sz;
     332             : 
     333      102936 :   if( rw->ref->accdb_type==FD_ACCDB_TYPE_V1 ) {
     334      102931 :     fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
     335      102931 :     rec->val_sz = (uint)( sizeof(fd_account_meta_t)+data_sz ) & FD_FUNK_REC_VAL_MAX;
     336      102931 :   }
     337      102936 : }
     338             : 
     339             : fd_accdb_user_vt_t const fd_accdb_user_v1_vt = {
     340             :   .fini            = fd_accdb_user_v1_fini,
     341             :   .batch_max       = fd_accdb_user_v1_batch_max,
     342             :   .open_ro_multi   = fd_accdb_user_v1_open_ro_multi,
     343             :   .open_rw_multi   = fd_accdb_user_v1_open_rw_multi,
     344             :   .close_ref_multi = fd_accdb_user_v1_close_ref_multi,
     345             :   .rw_data_max     = fd_accdb_user_v1_rw_data_max,
     346             :   .rw_data_sz_set  = fd_accdb_user_v1_rw_data_sz_set
     347             : };
     348             : 
     349             : fd_accdb_user_t *
     350             : fd_accdb_user_v1_init( fd_accdb_user_t * accdb,
     351             :                        void *            shfunk,
     352             :                        void *            shlocks,
     353          12 :                        ulong             max_depth ) {
     354          12 :   fd_accdb_user_v1_t * ljoin = (fd_accdb_user_v1_t *)accdb;
     355             : 
     356          12 :   if( FD_UNLIKELY( !ljoin ) ) {
     357           0 :     FD_LOG_WARNING(( "NULL ljoin" ));
     358           0 :     return NULL;
     359           0 :   }
     360          12 :   if( FD_UNLIKELY( !shfunk ) ) {
     361           0 :     FD_LOG_WARNING(( "NULL shfunk" ));
     362           0 :     return NULL;
     363           0 :   }
     364             : 
     365          12 :   memset( ljoin, 0, sizeof(fd_accdb_user_v1_t) );
     366          12 :   if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk, shlocks ) ) ) {
     367           0 :     FD_LOG_CRIT(( "fd_funk_join failed" ));
     368           0 :   }
     369             : 
     370          12 :   ljoin->lineage->max_depth = max_depth;
     371          12 :   accdb->base.accdb_type    = FD_ACCDB_TYPE_V1;
     372          12 :   accdb->base.vt            = &fd_accdb_user_v1_vt;
     373          12 :   return accdb;
     374          12 : }
     375             : 
     376             : fd_funk_t *
     377        5566 : fd_accdb_user_v1_funk( fd_accdb_user_t * accdb ) {
     378        5566 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     379        5566 :   uint accdb_type = accdb->base.accdb_type;
     380        5566 :   if( FD_UNLIKELY( accdb_type!=FD_ACCDB_TYPE_V1 && accdb_type!=FD_ACCDB_TYPE_V2 ) ) {
     381           0 :     FD_LOG_CRIT(( "fd_accdb_user_v1_funk called on non-v1 accdb_user (type %u)", accdb->base.accdb_type ));
     382           0 :   }
     383        5566 :   return v1->funk;
     384        5566 : }

Generated by: LCOV version 1.14