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 : }
|