LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_impl_v1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 228 336 67.9 %
Date: 2025-12-06 04:45:29 Functions: 12 16 75.0 %

          Line data    Source code
       1             : #include "fd_accdb_impl_v1.h"
       2             : #include "fd_accdb_sync.h"
       3             : 
       4             : FD_STATIC_ASSERT( alignof(fd_accdb_user_v1_t)<=alignof(fd_accdb_user_t), layout );
       5             : FD_STATIC_ASSERT( sizeof (fd_accdb_user_v1_t)<=sizeof(fd_accdb_user_t),  layout );
       6             : 
       7             : static int
       8             : fd_accdb_has_xid( fd_accdb_user_v1_t const * accdb,
       9         456 :                   fd_funk_txn_xid_t const *  rec_xid ) {
      10         456 :   ulong const fork_depth = accdb->fork_depth;
      11         462 :   for( ulong i=0UL; i<fork_depth; i++ ) {
      12         462 :     if( fd_funk_txn_xid_eq( &accdb->fork[i], rec_xid ) ) return 1;
      13         462 :   }
      14           0 :   return 0;
      15         456 : }
      16             : 
      17             : static int
      18             : fd_accdb_search_chain( fd_accdb_user_v1_t const * accdb,
      19             :                        ulong                      chain_idx,
      20             :                        fd_funk_rec_key_t const *  key,
      21         498 :                        fd_funk_rec_t **           out_rec ) {
      22         498 :   *out_rec = NULL;
      23             : 
      24         498 :   fd_funk_rec_map_shmem_t const *               shmap     = accdb->funk->rec_map->map;
      25         498 :   fd_funk_rec_map_shmem_private_chain_t const * chain_tbl = fd_funk_rec_map_shmem_private_chain_const( shmap, 0UL );
      26         498 :   fd_funk_rec_map_shmem_private_chain_t const * chain     = chain_tbl + chain_idx;
      27         498 :   fd_funk_rec_t *                               rec_tbl   = accdb->funk->rec_pool->ele;
      28         498 :   ulong                                         rec_max   = fd_funk_rec_pool_ele_max( accdb->funk->rec_pool );
      29         498 :   ulong                                         ver_cnt   = FD_VOLATILE_CONST( chain->ver_cnt );
      30             : 
      31             :   /* Start a speculative transaction for the chain containing revisions
      32             :      of the account key we are looking for. */
      33         498 :   ulong cnt = fd_funk_rec_map_private_vcnt_cnt( ver_cnt );
      34         498 :   if( FD_UNLIKELY( fd_funk_rec_map_private_vcnt_ver( ver_cnt )&1 ) ) {
      35           0 :     return FD_MAP_ERR_AGAIN; /* chain is locked */
      36           0 :   }
      37         498 :   FD_COMPILER_MFENCE();
      38         498 :   uint ele_idx = chain->head_cidx;
      39             : 
      40             :   /* Walk the map chain, bail at the first entry
      41             :      (Each chain is sorted newest-to-oldest) */
      42         498 :   fd_funk_rec_t * best = NULL;
      43         498 :   for( ulong i=0UL; i<cnt; i++, ele_idx=rec_tbl[ ele_idx ].map_next ) {
      44         456 :     fd_funk_rec_t * rec = &rec_tbl[ ele_idx ];
      45             : 
      46             :     /* Skip over unrelated records (hash collision) */
      47         456 :     if( FD_UNLIKELY( !fd_funk_rec_key_eq( rec->pair.key, key ) ) ) continue;
      48             : 
      49             :     /* Confirm that record is part of the current fork
      50             :        FIXME this has bad performance / pointer-chasing */
      51         456 :     if( FD_UNLIKELY( !fd_accdb_has_xid( accdb, rec->pair.xid ) ) ) continue;
      52             : 
      53         456 :     if( FD_UNLIKELY( rec->map_next==ele_idx ) ) {
      54           0 :       FD_LOG_CRIT(( "fd_accdb_search_chain detected cycle" ));
      55           0 :     }
      56         456 :     if( rec->map_next > rec_max ) {
      57         456 :       if( FD_UNLIKELY( !fd_funk_rec_map_private_idx_is_null( rec->map_next ) ) ) {
      58           0 :         FD_LOG_CRIT(( "fd_accdb_search_chain detected memory corruption: rec->map_next %u is out of bounds (rec_max %lu)",
      59           0 :                       rec->map_next, rec_max ));
      60           0 :       }
      61         456 :     }
      62         456 :     best = rec;
      63         456 :     break;
      64         456 :   }
      65             : 
      66             :   /* Retry if we were overrun */
      67         498 :   if( FD_UNLIKELY( FD_VOLATILE_CONST( chain->ver_cnt )!=ver_cnt ) ) {
      68           0 :     return FD_MAP_ERR_AGAIN;
      69           0 :   }
      70             : 
      71         498 :   *out_rec = best;
      72         498 :   return FD_MAP_SUCCESS;
      73         498 : }
      74             : 
      75             : void
      76             : fd_accdb_load_fork_slow( fd_accdb_user_v1_t *      accdb,
      77          45 :                          fd_funk_txn_xid_t const * xid ) {
      78          45 :   fd_funk_txn_xid_t next_xid = *xid;
      79             : 
      80             :   /* Walk transaction graph, recovering from overruns on-the-fly */
      81          45 :   accdb->fork_depth = 0UL;
      82             : 
      83          45 :   ulong txn_max = fd_funk_txn_pool_ele_max( accdb->funk->txn_pool );
      84          45 :   ulong i;
      85          48 :   for( i=0UL; i<FD_ACCDB_DEPTH_MAX; i++ ) {
      86          48 :     fd_funk_txn_map_query_t query[1];
      87          48 :     fd_funk_txn_t const *   candidate;
      88          48 :     fd_funk_txn_xid_t       found_xid;
      89          48 :     ulong                   parent_idx;
      90          48 :     fd_funk_txn_xid_t       parent_xid;
      91          48 : retry:
      92             :     /* Speculatively look up transaction from map */
      93          48 :     for(;;) {
      94          48 :       int query_err = fd_funk_txn_map_query_try( accdb->funk->txn_map, &next_xid, NULL, query, 0 );
      95          48 :       if( FD_UNLIKELY( query_err==FD_MAP_ERR_AGAIN ) ) {
      96             :         /* FIXME random backoff */
      97           0 :         FD_SPIN_PAUSE();
      98           0 :         continue;
      99           0 :       }
     100          48 :       if( query_err==FD_MAP_ERR_KEY ) goto done;
     101          48 :       if( FD_UNLIKELY( query_err!=FD_MAP_SUCCESS ) ) {
     102           0 :         FD_LOG_CRIT(( "fd_funk_txn_map_query_try failed: %i-%s", query_err, fd_map_strerror( query_err ) ));
     103           0 :       }
     104          48 :       break;
     105          48 :     }
     106             : 
     107             :     /* Lookup parent transaction while recovering from overruns
     108             :        FIXME This would be a lot easier if transactions specified
     109             :              parent by XID instead of by pointer ... */
     110          48 :     candidate = fd_funk_txn_map_query_ele_const( query );
     111          48 :     FD_COMPILER_MFENCE();
     112          48 :     do {
     113          48 :       found_xid  = FD_VOLATILE_CONST( candidate->xid );
     114          48 :       parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) );
     115          48 :       if( fd_funk_txn_idx_is_null( parent_idx ) ) break;
     116           3 :       if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx ));
     117             : 
     118           3 :       FD_COMPILER_MFENCE();
     119           3 :       fd_funk_txn_t const * parent = &accdb->funk->txn_pool->ele[ parent_idx ];
     120           3 :       parent_xid = FD_VOLATILE_CONST( parent->xid );
     121           3 :       FD_COMPILER_MFENCE();
     122             : 
     123           3 :       parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) );
     124           3 :       if( fd_funk_txn_idx_is_null( parent_idx ) ) break;
     125           3 :       if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx ));
     126           3 :     } while(0);
     127          48 :     FD_COMPILER_MFENCE();
     128             : 
     129             :     /* Verify speculative loads by ensuring txn still exists in map */
     130          48 :     if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
     131           0 :       FD_SPIN_PAUSE();
     132           0 :       goto retry;
     133           0 :     }
     134             : 
     135          48 :     if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &found_xid, &next_xid ) ) ) {
     136           0 :       FD_LOG_CRIT(( "fd_accdb_load_fork_slow detected memory corruption: expected xid %lu:%lu at %p, found %lu:%lu",
     137           0 :                     next_xid.ul[0], next_xid.ul[1],
     138           0 :                     (void *)candidate,
     139           0 :                     found_xid.ul[0], found_xid.ul[1] ));
     140           0 :     }
     141             : 
     142          48 :     accdb->fork[ i ] = next_xid;
     143          48 :     if( fd_funk_txn_idx_is_null( parent_idx ) ) {
     144             :       /* Reached root */
     145          45 :       i++;
     146          45 :       break;
     147          45 :     }
     148           3 :     next_xid = parent_xid;
     149           3 :   }
     150             : 
     151          45 : done:
     152          45 :   accdb->fork_depth = i;
     153          45 :   if( FD_UNLIKELY( accdb->fork_depth==FD_ACCDB_DEPTH_MAX ) ) {
     154           0 :     FD_LOG_CRIT(( "Account database fork depth exceeded max of %lu", FD_ACCDB_DEPTH_MAX ));
     155           0 :   }
     156             : 
     157             :   /* FIXME crash if fork depth greater than cache depth */
     158          45 :   if( accdb->fork_depth < FD_ACCDB_DEPTH_MAX ) {
     159          45 :     fd_funk_txn_xid_set_root( &accdb->fork[ accdb->fork_depth++ ] );
     160          45 :   }
     161             : 
     162             :   /* Remember tip fork */
     163          45 :   fd_funk_txn_t * tip = fd_funk_txn_query( xid, accdb->funk->txn_map );
     164          45 :   ulong tip_idx = tip ? (ulong)( tip-accdb->funk->txn_pool->ele ) : ULONG_MAX;
     165          45 :   accdb->tip_txn_idx = tip_idx;
     166          45 :   if( tip ) fd_funk_txn_state_assert( tip, FD_FUNK_TXN_STATE_ACTIVE );
     167          45 : }
     168             : 
     169             : static inline void
     170             : fd_accdb_load_fork( fd_accdb_user_v1_t *      accdb,
     171         498 :                     fd_funk_txn_xid_t const * xid ) {
     172             :   /* Skip if already on the correct fork */
     173         498 :   if( FD_LIKELY( (!!accdb->fork_depth) & (!!fd_funk_txn_xid_eq( &accdb->fork[ 0 ], xid ) ) ) ) return;
     174          45 :   if( FD_UNLIKELY( accdb->base.rw_active ) ) {
     175           0 :     FD_LOG_CRIT(( "Invariant violation: all active account references of an accdb_user must be accessed through the same XID (active XID %lu:%lu, requested XID %lu:%lu)",
     176           0 :                   accdb->fork[0].ul[0], accdb->fork[0].ul[1],
     177           0 :                   xid          ->ul[0], xid          ->ul[1] ));
     178           0 :   }
     179          45 :   fd_accdb_load_fork_slow( accdb, xid ); /* switch fork */
     180          45 : }
     181             : 
     182             : fd_accdb_peek_t *
     183             : fd_accdb_peek_funk( fd_accdb_user_v1_t *      accdb,
     184             :                     fd_accdb_peek_t *         peek,
     185             :                     fd_funk_txn_xid_t const * xid,
     186         498 :                     void const *              address ) {
     187         498 :   fd_funk_t const * funk = accdb->funk;
     188         498 :   fd_funk_rec_key_t key[1]; memcpy( key->uc, address, 32UL );
     189             : 
     190             :   /* Hash key to chain */
     191         498 :   fd_funk_xid_key_pair_t pair[1];
     192         498 :   fd_funk_txn_xid_copy( pair->xid, xid );
     193         498 :   fd_funk_rec_key_copy( pair->key, key );
     194         498 :   fd_funk_rec_map_t const * rec_map = funk->rec_map;
     195         498 :   ulong hash      = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
     196         498 :   ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
     197             : 
     198             :   /* Traverse chain for candidate */
     199         498 :   fd_funk_rec_t * rec = NULL;
     200         498 :   for(;;) {
     201         498 :     int err = fd_accdb_search_chain( accdb, chain_idx, key, &rec );
     202         498 :     if( FD_LIKELY( err==FD_MAP_SUCCESS ) ) break;
     203           0 :     FD_SPIN_PAUSE();
     204             :     /* FIXME backoff */
     205           0 :   }
     206         498 :   if( !rec ) return NULL;
     207             : 
     208         456 :   *peek = (fd_accdb_peek_t) {
     209         456 :     .acc = {{
     210         456 :       .rec  = rec,
     211         456 :       .meta = fd_funk_val( rec, funk->wksp )
     212         456 :     }},
     213         456 :     .spec = {{
     214         456 :       .key  = *key,
     215         456 :       .keyp = rec->pair.key
     216         456 :     }}
     217         456 :   };
     218         456 :   return peek;
     219         498 : }
     220             : 
     221             : fd_accdb_peek_t *
     222             : fd_accdb_user_v1_peek( fd_accdb_user_t *         accdb,
     223             :                        fd_accdb_peek_t *         peek,
     224             :                        fd_funk_txn_xid_t const * xid,
     225           0 :                        void const *              address ) {
     226           0 :   if( FD_UNLIKELY( !accdb ) ) FD_LOG_CRIT(( "NULL accdb" ));
     227           0 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     228           0 :   if( FD_UNLIKELY( !v1->funk->shmem ) ) FD_LOG_CRIT(( "NULL funk shmem" ));
     229           0 :   fd_accdb_load_fork( v1, xid );
     230           0 :   if( !fd_accdb_peek_funk( v1, peek, xid, address ) ) return NULL;
     231           0 :   if( FD_UNLIKELY( !peek->acc->meta->lamports ) ) return NULL;
     232           0 :   return peek;
     233           0 : }
     234             : 
     235             : static void
     236             : fd_accdb_copy_account( fd_account_meta_t *   out_meta,
     237             :                        void *                out_data,
     238           3 :                        fd_accdb_ro_t const * acc ) {
     239           3 :   memset( out_meta, 0, sizeof(fd_account_meta_t) );
     240           3 :   out_meta->lamports = fd_accdb_ref_lamports( acc );
     241           3 :   if( FD_LIKELY( out_meta->lamports ) ) {
     242           3 :     memcpy( out_meta->owner, fd_accdb_ref_owner( acc ), 32UL );
     243           3 :     out_meta->executable = !!fd_accdb_ref_exec_bit( acc );
     244           3 :     out_meta->dlen       = (uint)fd_accdb_ref_data_sz( acc );
     245           3 :     fd_memcpy( out_data, fd_accdb_ref_data_const( acc ), out_meta->dlen );
     246           3 :   }
     247           3 : }
     248             : 
     249             : /* fd_accdb_prep_create preps a writable handle for a newly created
     250             :    account. */
     251             : 
     252             : static fd_accdb_rw_t *
     253             : fd_accdb_prep_create( fd_accdb_rw_t *           rw,
     254             :                       fd_accdb_user_v1_t *      accdb,
     255             :                       fd_funk_txn_xid_t const * xid,
     256             :                       void const *              address,
     257             :                       void *                    val,
     258             :                       ulong                     val_sz,
     259          45 :                       ulong                     val_max ) {
     260          45 :   fd_funk_rec_t * rec = fd_funk_rec_pool_acquire( accdb->funk->rec_pool, NULL, 1, NULL );
     261          45 :   if( FD_UNLIKELY( !rec ) ) FD_LOG_CRIT(( "Failed to modify account: DB record pool is out of memory" ));
     262             : 
     263          45 :   memset( rec, 0, sizeof(fd_funk_rec_t) );
     264          45 :   rec->val_gaddr = fd_wksp_gaddr_fast( accdb->funk->wksp, val );
     265          45 :   rec->val_sz    = (uint)( fd_ulong_min( val_sz,  FD_FUNK_REC_VAL_MAX ) & FD_FUNK_REC_VAL_MAX );
     266          45 :   rec->val_max   = (uint)( fd_ulong_min( val_max, FD_FUNK_REC_VAL_MAX ) & FD_FUNK_REC_VAL_MAX );
     267          45 :   memcpy( rec->pair.key->uc, address, 32UL );
     268          45 :   fd_funk_txn_xid_copy( rec->pair.xid, xid );
     269          45 :   rec->tag      = 0;
     270          45 :   rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     271          45 :   rec->next_idx = FD_FUNK_REC_IDX_NULL;
     272             : 
     273          45 :   fd_account_meta_t * meta = val;
     274          45 :   meta->slot = xid->ul[0];
     275             : 
     276          45 :   accdb->base.rw_active++;
     277          45 :   *rw = (fd_accdb_rw_t) {
     278          45 :     .rec       = rec,
     279          45 :     .meta      = meta,
     280          45 :     .published = 0
     281          45 :   };
     282          45 :   return rw;
     283          45 : }
     284             : 
     285             : /* fd_accdb_prep_inplace preps a writable handle for a mutable record. */
     286             : 
     287             : static fd_accdb_rw_t *
     288             : fd_accdb_prep_inplace( fd_accdb_rw_t *      rw,
     289             :                        fd_accdb_user_v1_t * accdb,
     290         453 :                        fd_funk_rec_t *      rec ) {
     291             :   /* Take the opportunity to run some validation checks */
     292         453 :   if( FD_UNLIKELY( !rec->val_gaddr ) ) {
     293           0 :     FD_LOG_CRIT(( "Failed to prepare in-place account write: rec %p is not allocated", (void *)rec ));
     294           0 :   }
     295             : 
     296         453 :   accdb->base.rw_active++;
     297         453 :   *rw = (fd_accdb_rw_t) {
     298         453 :     .rec       = rec,
     299         453 :     .meta      = fd_funk_val( rec, accdb->funk->wksp ),
     300         453 :     .published = 1
     301         453 :   };
     302         453 :   if( FD_UNLIKELY( !rw->meta->lamports ) ) {
     303           0 :     memset( rw->meta, 0, sizeof(fd_account_meta_t) );
     304           0 :   }
     305         453 :   return rw;
     306         453 : }
     307             : 
     308             : void
     309          54 : fd_accdb_user_v1_fini( fd_accdb_user_t * accdb ) {
     310          54 :   fd_accdb_user_v1_t * user = (fd_accdb_user_v1_t *)accdb;
     311             : 
     312          54 :   if( FD_UNLIKELY( !fd_funk_leave( user->funk, NULL ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
     313          54 : }
     314             : 
     315             : fd_accdb_ro_t *
     316             : fd_accdb_user_v1_open_ro( fd_accdb_user_t *         accdb,
     317             :                           fd_accdb_ro_t *           ro,
     318             :                           fd_funk_txn_xid_t const * xid,
     319           0 :                           void const *              address ) {
     320           0 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     321           0 :   fd_accdb_load_fork( v1, xid );
     322             : 
     323           0 :   fd_accdb_peek_t peek[1];
     324           0 :   if( !fd_accdb_peek_funk( v1, peek, xid, address ) ) return NULL;
     325           0 :   if( FD_UNLIKELY( !peek->acc->meta->lamports ) ) return NULL;
     326             : 
     327           0 :   v1->base.ro_active++;
     328           0 :   *ro = *peek->acc;
     329           0 :   return ro;
     330           0 : }
     331             : 
     332             : void
     333             : fd_accdb_user_v1_close_ro( fd_accdb_user_t * accdb,
     334           0 :                            fd_accdb_ro_t *   ro ) {
     335           0 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     336             : 
     337           0 :   v1->base.ro_active--;
     338           0 :   (void)ro;
     339           0 : }
     340             : 
     341             : fd_accdb_rw_t *
     342             : fd_accdb_user_v1_open_rw( fd_accdb_user_t *         accdb,
     343             :                           fd_accdb_rw_t *           rw,
     344             :                           fd_funk_txn_xid_t const * xid,
     345             :                           void const *              address,
     346             :                           ulong                     data_max,
     347         498 :                           int                       do_create ) {
     348         498 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     349             : 
     350             :   /* Pivot to different fork */
     351         498 :   fd_accdb_load_fork( v1, xid );
     352         498 :   ulong txn_idx = v1->tip_txn_idx;
     353         498 :   if( FD_UNLIKELY( txn_idx==ULONG_MAX ) ) {
     354           0 :     FD_LOG_CRIT(( "fd_accdb_user_v1_open_rw failed: XID %lu:%lu is rooted", xid->ul[0], xid->ul[1] ));
     355           0 :   }
     356         498 :   if( FD_UNLIKELY( txn_idx >= fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) ) ) {
     357           0 :     FD_LOG_CRIT(( "memory corruption detected: invalid txn_idx %lu (max %lu)",
     358           0 :                   txn_idx, fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) ));
     359           0 :   }
     360         498 :   fd_funk_txn_t * txn = &v1->funk->txn_pool->ele[ txn_idx ];
     361         498 :   if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) {
     362           0 :     FD_LOG_CRIT(( "Failed to modify account: data race detected on fork node (expected XID %lu:%lu, found %lu:%lu)",
     363           0 :                   xid->ul[0],     xid->ul[1],
     364           0 :                   txn->xid.ul[0], txn->xid.ul[1] ));
     365           0 :   }
     366         498 :   if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
     367           0 :     FD_LOG_CRIT(( "Failed to modify account: XID %lu:%lu has children/is frozen", xid->ul[0], xid->ul[1] ));
     368           0 :   }
     369             : 
     370             :   /* Query old record value */
     371             : 
     372         498 :   fd_accdb_peek_t peek[1];
     373         498 :   if( FD_UNLIKELY( !fd_accdb_peek_funk( v1, peek, xid, address ) ) ) {
     374             : 
     375             :     /* Record not found */
     376          42 :     if( !do_create ) return NULL;
     377          42 :     ulong  val_sz_min = sizeof(fd_account_meta_t)+data_max;
     378          42 :     ulong  val_max = 0UL;
     379          42 :     void * val     = fd_alloc_malloc_at_least( v1->funk->alloc, 16UL, val_sz_min, &val_max );
     380          42 :     if( FD_UNLIKELY( !val ) ) {
     381           0 :       FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", data_max ));
     382           0 :     }
     383          42 :     fd_memset( val, 0, val_sz_min );
     384          42 :     return fd_accdb_prep_create( rw, v1, xid, address, val, val_sz_min, val_max );
     385             : 
     386         456 :   } else if( fd_funk_txn_xid_eq( peek->acc->rec->pair.xid, xid ) ) {
     387             : 
     388             :     /* Mutable record found, modify in-place */
     389         453 :     fd_funk_rec_t * rec = (void *)( peek->acc->ref->rec_laddr );
     390         453 :     ulong  acc_orig_sz = fd_accdb_ref_data_sz( peek->acc );
     391         453 :     ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
     392         453 :     void * val         = fd_funk_val_truncate( rec, v1->funk->alloc, v1->funk->wksp, 16UL, val_sz_min, NULL );
     393         453 :     if( FD_UNLIKELY( !val ) ) {
     394           0 :       FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     395           0 :     }
     396         453 :     return fd_accdb_prep_inplace( rw, v1, rec );
     397             : 
     398         453 :   } else {
     399             : 
     400             :     /* Frozen record found, copy out to new object */
     401           3 :     ulong  acc_orig_sz = fd_accdb_ref_data_sz( peek->acc );
     402           3 :     ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
     403           3 :     ulong  val_sz      = peek->acc->rec->val_sz;
     404           3 :     ulong  val_max     = 0UL;
     405           3 :     void * val         = fd_alloc_malloc_at_least( v1->funk->alloc, 16UL, val_sz_min, &val_max );
     406           3 :     if( FD_UNLIKELY( !val ) ) {
     407           0 :       FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     408           0 :     }
     409             : 
     410           3 :     fd_account_meta_t * meta            = val;
     411           3 :     uchar *             data            = (uchar *)( meta+1 );
     412           3 :     ulong               data_max_actual = val_max - sizeof(fd_account_meta_t);
     413           3 :     fd_accdb_copy_account( meta, data, peek->acc );
     414           3 :     if( acc_orig_sz<data_max_actual ) {
     415             :       /* Zero out trailing data */
     416           3 :       uchar * tail    = data           +acc_orig_sz;
     417           3 :       ulong   tail_sz = data_max_actual-acc_orig_sz;
     418           3 :       fd_memset( tail, 0, tail_sz );
     419           3 :     }
     420           3 :     if( FD_UNLIKELY( !fd_accdb_peek_test( peek ) ) ) {
     421           0 :       FD_LOG_CRIT(( "Failed to modify account: data race detected, account was removed while being read" ));
     422           0 :     }
     423             : 
     424           3 :     return fd_accdb_prep_create( rw, v1, xid, address, val, val_sz, val_max );
     425             : 
     426           3 :   }
     427         498 : }
     428             : 
     429             : void
     430             : fd_accdb_user_v1_close_rw( fd_accdb_user_t * accdb,
     431           0 :                            fd_accdb_rw_t *   write ) {
     432           0 :   if( FD_UNLIKELY( !accdb ) ) FD_LOG_CRIT(( "NULL accdb" ));
     433           0 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     434             : 
     435           0 :   if( FD_UNLIKELY( !v1->base.rw_active ) ) {
     436           0 :     FD_LOG_CRIT(( "Failed to modify account: ref count underflow" ));
     437           0 :   }
     438             : 
     439           0 :   if( !write->published ) {
     440           0 :     if( FD_UNLIKELY( v1->tip_txn_idx==ULONG_MAX ) ) {
     441           0 :       FD_LOG_CRIT(( "accdb_user corrupt: not joined to a transaction" ));
     442           0 :     }
     443           0 :     fd_funk_txn_t * txn = v1->funk->txn_pool->ele + v1->tip_txn_idx;
     444           0 :     fd_funk_rec_prepare_t prepare = {
     445           0 :       .rec          = write->rec,
     446           0 :       .rec_head_idx = &txn->rec_head_idx,
     447           0 :       .rec_tail_idx = &txn->rec_tail_idx
     448           0 :     };
     449           0 :     fd_funk_rec_publish( v1->funk, &prepare );
     450           0 :   }
     451             : 
     452           0 :   v1->base.rw_active--;
     453           0 : }
     454             : 
     455             : fd_accdb_user_vt_t const fd_accdb_user_v1_vt = {
     456             :   .fini     = fd_accdb_user_v1_fini,
     457             :   .peek     = fd_accdb_user_v1_peek,
     458             :   .open_ro  = fd_accdb_user_v1_open_ro,
     459             :   .close_ro = fd_accdb_user_v1_close_ro,
     460             :   .open_rw  = fd_accdb_user_v1_open_rw,
     461             :   .close_rw = fd_accdb_user_v1_close_rw
     462             : };
     463             : 
     464             : fd_accdb_user_t *
     465             : fd_accdb_user_v1_init( fd_accdb_user_t * accdb,
     466          57 :                        void *            shfunk ) {
     467          57 :   fd_accdb_user_v1_t * ljoin = (fd_accdb_user_v1_t *)accdb;
     468             : 
     469          57 :   if( FD_UNLIKELY( !ljoin ) ) {
     470           0 :     FD_LOG_WARNING(( "NULL ljoin" ));
     471           0 :     return NULL;
     472           0 :   }
     473          57 :   if( FD_UNLIKELY( !shfunk ) ) {
     474           0 :     FD_LOG_WARNING(( "NULL shfunk" ));
     475           0 :     return NULL;
     476           0 :   }
     477             : 
     478          57 :   memset( ljoin, 0, sizeof(fd_accdb_user_v1_t) );
     479          57 :   if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk ) ) ) {
     480           0 :     FD_LOG_CRIT(( "fd_funk_join failed" ));
     481           0 :   }
     482             : 
     483          57 :   accdb->base.accdb_type = FD_ACCDB_TYPE_V1;
     484          57 :   accdb->base.vt         = &fd_accdb_user_v1_vt;
     485          57 :   return accdb;
     486          57 : }
     487             : 
     488             : fd_funk_t *
     489         567 : fd_accdb_user_v1_funk( fd_accdb_user_t * accdb ) {
     490         567 :   fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
     491         567 :   uint accdb_type = accdb->base.accdb_type;
     492         567 :   if( FD_UNLIKELY( accdb_type!=FD_ACCDB_TYPE_V1 && accdb_type!=FD_ACCDB_TYPE_V2 ) ) {
     493           0 :     FD_LOG_CRIT(( "fd_accdb_user_v1_funk called on non-v1 accdb_user (type %u)", accdb->base.accdb_type ));
     494           0 :   }
     495         567 :   return v1->funk;
     496         567 : }

Generated by: LCOV version 1.14