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

Generated by: LCOV version 1.14