Line data Source code
1 : #include "fd_funkier.h"
2 :
3 : /* Provide the actual record map implementation */
4 :
5 : #define POOL_NAME fd_funkier_rec_pool
6 0 : #define POOL_ELE_T fd_funkier_rec_t
7 : #define POOL_IDX_T uint
8 0 : #define POOL_NEXT map_next
9 : #define POOL_IMPL_STYLE 2
10 : #include "../util/tmpl/fd_pool_para.c"
11 :
12 : #define MAP_NAME fd_funkier_rec_map
13 0 : #define MAP_ELE_T fd_funkier_rec_t
14 0 : #define MAP_KEY_T fd_funkier_xid_key_pair_t
15 0 : #define MAP_KEY pair
16 : #define MAP_KEY_EQ(k0,k1) fd_funkier_xid_key_pair_eq((k0),(k1))
17 : #define MAP_KEY_HASH(k0,seed) fd_funkier_xid_key_pair_hash((k0),(seed))
18 0 : #define MAP_NEXT map_next
19 0 : #define MAP_MEMO map_hash
20 0 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
21 : #define MAP_MEMOIZE 1
22 : #define MAP_IMPL_STYLE 2
23 : #include "../util/tmpl/fd_map_para.c"
24 :
25 : fd_funkier_rec_t const *
26 : fd_funkier_rec_query_try( fd_funkier_t * funk,
27 : fd_funkier_txn_t const * txn,
28 : fd_funkier_rec_key_t const * key,
29 0 : fd_funkier_rec_query_t * query ) {
30 : #ifdef FD_FUNKIER_HANDHOLDING
31 : if( FD_UNLIKELY( funk==NULL || key==NULL || query==NULL ) ) {
32 : return NULL;
33 : }
34 : if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
35 : return NULL;
36 : }
37 : #endif
38 :
39 0 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
40 0 : fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
41 0 : fd_funkier_xid_key_pair_t pair[1];
42 0 : if( txn == NULL ) {
43 0 : fd_funkier_txn_xid_set_root( pair->xid );
44 0 : } else {
45 0 : fd_funkier_txn_xid_copy( pair->xid, &txn->xid );
46 0 : }
47 0 : fd_funkier_rec_key_copy( pair->key, key );
48 0 : for(;;) {
49 0 : int err = fd_funkier_rec_map_query_try( &rec_map, pair, NULL, query );
50 0 : if( err == FD_MAP_SUCCESS ) break;
51 0 : if( err == FD_MAP_ERR_KEY ) return NULL;
52 0 : if( err == FD_MAP_ERR_AGAIN ) continue;
53 0 : FD_LOG_CRIT(( "query returned err %d", err ));
54 0 : }
55 0 : return fd_funkier_rec_map_query_ele_const( query );
56 0 : }
57 :
58 : fd_funkier_rec_t const *
59 : fd_funkier_rec_query_try_global( fd_funkier_t * funk,
60 : fd_funkier_txn_t const * txn,
61 : fd_funkier_rec_key_t const * key,
62 : fd_funkier_txn_t const ** txn_out,
63 0 : fd_funkier_rec_query_t * query ) {
64 : #ifdef FD_FUNKIER_HANDHOLDING
65 : if( FD_UNLIKELY( funk==NULL || key==NULL || query==NULL ) ) {
66 : return NULL;
67 : }
68 : if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
69 : return NULL;
70 : }
71 : #endif
72 :
73 : /* Look for the first element in the hash chain with the right
74 : record key. This takes advantage of the fact that elements with
75 : the same record key appear on the same hash chain in order of
76 : newest to oldest. */
77 :
78 0 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
79 0 : fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
80 0 : fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( funk, wksp );
81 :
82 0 : fd_funkier_xid_key_pair_t pair[1];
83 0 : if( txn == NULL ) {
84 0 : fd_funkier_txn_xid_set_root( pair->xid );
85 0 : } else {
86 0 : fd_funkier_txn_xid_copy( pair->xid, &txn->xid );
87 0 : }
88 0 : fd_funkier_rec_key_copy( pair->key, key );
89 0 : ulong hash = fd_funkier_rec_map_key_hash( pair, rec_map.map->seed );
90 0 : ulong chain_idx = (hash & (rec_map.map->chain_cnt-1UL) );
91 0 : if( fd_funkier_rec_map_iter_lock( &rec_map, &chain_idx, 1, FD_MAP_FLAG_BLOCKING) ) {
92 0 : FD_LOG_CRIT(( "failed to lock hash chain" ));
93 0 : }
94 :
95 0 : fd_funkier_rec_map_shmem_private_chain_t * chain = (fd_funkier_rec_map_shmem_private_chain_t *)(rec_map.map+1) + chain_idx;
96 0 : query->ele = NULL;
97 0 : query->chain = chain;
98 0 : query->ver_cnt = chain->ver_cnt + (1UL<<43U); /* After unlock */
99 :
100 0 : for( fd_funkier_rec_map_iter_t iter = fd_funkier_rec_map_iter( &rec_map, chain_idx );
101 0 : !fd_funkier_rec_map_iter_done( iter );
102 0 : iter = fd_funkier_rec_map_iter_next( iter ) ) {
103 0 : fd_funkier_rec_t const * ele = fd_funkier_rec_map_iter_ele_const( iter );
104 0 : if( FD_LIKELY( hash == ele->map_hash ) && FD_LIKELY( fd_funkier_rec_key_eq( key, ele->pair.key ) ) ) {
105 :
106 : /* For cur_txn in path from [txn] to [root] where root is NULL */
107 :
108 0 : for( fd_funkier_txn_t const * cur_txn = txn; ; cur_txn = fd_funkier_txn_parent( cur_txn, &txn_pool ) ) {
109 : /* If record ele is part of transaction cur_txn, we have a
110 : match. According to the property above, this will be the
111 : youngest descendent in the transaction stack. */
112 :
113 0 : int match = FD_UNLIKELY( cur_txn ) ? /* opt for root find (FIXME: eliminate branch with cmov into txn_xid_eq?) */
114 0 : fd_funkier_txn_xid_eq( &cur_txn->xid, ele->pair.xid ) :
115 0 : fd_funkier_txn_xid_eq_root( ele->pair.xid );
116 :
117 0 : if( FD_LIKELY( match ) ) {
118 0 : if( txn_out ) *txn_out = cur_txn;
119 0 : query->ele = ( FD_UNLIKELY( ele->flags & FD_FUNKIER_REC_FLAG_ERASE ) ? NULL :
120 0 : (fd_funkier_rec_t *)ele );
121 0 : fd_funkier_rec_map_iter_unlock( &rec_map, &chain_idx, 1 );
122 0 : return query->ele;
123 0 : }
124 :
125 0 : if( cur_txn == NULL ) break;
126 0 : }
127 0 : }
128 0 : }
129 0 : fd_funkier_rec_map_iter_unlock( &rec_map, &chain_idx, 1 );
130 0 : return NULL;
131 0 : }
132 :
133 : int
134 0 : fd_funkier_rec_query_test( fd_funkier_rec_query_t * query ) {
135 0 : return fd_funkier_rec_map_query_test( query );
136 0 : }
137 :
138 : fd_funkier_rec_t *
139 : fd_funkier_rec_prepare( fd_funkier_t * funk,
140 : fd_funkier_txn_t * txn,
141 : fd_funkier_rec_key_t const * key,
142 : fd_funkier_rec_prepare_t * prepare,
143 0 : int * opt_err ) {
144 : #ifdef FD_FUNKIER_HANDHOLDING
145 : if( FD_UNLIKELY( funk==NULL || key==NULL || prepare==NULL ) ) {
146 : fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_INVAL );
147 : return NULL;
148 : }
149 : if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
150 : fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_INVAL );
151 : return NULL;
152 : }
153 : #endif
154 :
155 0 : if( !txn ) { /* Modifying last published */
156 0 : if( FD_UNLIKELY( fd_funkier_last_publish_is_frozen( funk ) ) ) {
157 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_FROZEN );
158 0 : return NULL;
159 0 : }
160 0 : } else {
161 0 : if( FD_UNLIKELY( fd_funkier_txn_is_frozen( txn ) ) ) {
162 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_FROZEN );
163 0 : return NULL;
164 0 : }
165 0 : }
166 :
167 0 : prepare->funk = funk;
168 0 : prepare->wksp = fd_funkier_wksp( funk );
169 0 : fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( funk, prepare->wksp );
170 0 : fd_funkier_rec_t * rec = prepare->rec = fd_funkier_rec_pool_acquire( &rec_pool, NULL, 1, opt_err );
171 0 : if( rec != NULL ) {
172 0 : if( txn == NULL ) {
173 0 : fd_funkier_txn_xid_set_root( rec->pair.xid );
174 0 : rec->txn_cidx = fd_funkier_txn_cidx( FD_FUNKIER_TXN_IDX_NULL );
175 0 : prepare->rec_head_idx = &funk->rec_head_idx;
176 0 : prepare->rec_tail_idx = &funk->rec_tail_idx;
177 0 : } else {
178 0 : fd_funkier_txn_xid_copy( rec->pair.xid, &txn->xid );
179 0 : fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( funk, prepare->wksp );
180 0 : rec->txn_cidx = fd_funkier_txn_cidx( (ulong)( txn - txn_pool.ele ) );
181 0 : prepare->rec_head_idx = &txn->rec_head_idx;
182 0 : prepare->rec_tail_idx = &txn->rec_tail_idx;
183 0 : }
184 0 : fd_funkier_rec_key_copy( rec->pair.key, key );
185 0 : fd_funkier_val_init( rec );
186 0 : rec->tag = 0;
187 0 : rec->flags = 0;
188 0 : rec->prev_idx = FD_FUNKIER_REC_IDX_NULL;
189 0 : rec->next_idx = FD_FUNKIER_REC_IDX_NULL;
190 0 : } else {
191 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_REC );
192 0 : }
193 0 : return rec;
194 0 : }
195 :
196 : void
197 0 : fd_funkier_rec_publish( fd_funkier_rec_prepare_t * prepare ) {
198 0 : fd_funkier_rec_t * rec = prepare->rec;
199 0 : ulong * rec_head_idx = prepare->rec_head_idx;
200 0 : ulong * rec_tail_idx = prepare->rec_tail_idx;
201 0 : fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( prepare->funk, prepare->wksp );
202 0 : fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( prepare->funk, prepare->wksp );
203 :
204 : /* Use the tail idx to establish an order even if there is concurrency */
205 0 : ulong rec_prev_idx;
206 0 : ulong rec_idx = (ulong)( rec - rec_pool.ele );
207 0 : for(;;) {
208 0 : rec_prev_idx = *rec_tail_idx;
209 0 : if( FD_ATOMIC_CAS( rec_tail_idx, rec_prev_idx, rec_idx ) == rec_prev_idx ) break;
210 0 : }
211 0 : rec->prev_idx = rec_prev_idx;
212 0 : if( fd_funkier_rec_idx_is_null( rec_prev_idx ) ) {
213 0 : *rec_head_idx = rec_idx;
214 0 : } else {
215 0 : rec_pool.ele[ rec_prev_idx ].next_idx = rec_idx;
216 0 : }
217 :
218 0 : if( fd_funkier_rec_map_insert( &rec_map, rec, FD_MAP_FLAG_BLOCKING ) ) {
219 0 : FD_LOG_CRIT(( "fd_funkier_rec_map_insert failed" ));
220 0 : }
221 0 : }
222 :
223 : void
224 0 : fd_funkier_rec_cancel( fd_funkier_rec_prepare_t * prepare ) {
225 0 : fd_funkier_val_flush( prepare->rec, fd_funkier_alloc( prepare->funk, prepare->wksp ), prepare->wksp );
226 0 : fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( prepare->funk, prepare->wksp );
227 0 : fd_funkier_rec_pool_release( &rec_pool, prepare->rec, 1 );
228 0 : }
229 :
230 : fd_funkier_rec_t *
231 : fd_funkier_rec_clone( fd_funkier_t * funk,
232 : fd_funkier_txn_t * txn,
233 : fd_funkier_rec_key_t const * key,
234 : fd_funkier_rec_prepare_t * prepare,
235 0 : int * opt_err ) {
236 0 : fd_funkier_rec_t * new_rec = fd_funkier_rec_prepare( funk, txn, key, prepare, opt_err );
237 0 : if( !new_rec ) return NULL;
238 :
239 0 : for(;;) {
240 0 : fd_funkier_rec_query_t query[1];
241 0 : fd_funkier_rec_t const * old_rec = fd_funkier_rec_query_try_global( funk, txn, key, NULL, query );
242 0 : if( !old_rec ) {
243 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_KEY );
244 0 : fd_funkier_rec_cancel( prepare );
245 0 : return NULL;
246 0 : }
247 :
248 0 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
249 0 : ulong val_sz = old_rec->val_sz;
250 0 : void * buf = fd_funkier_val_truncate( new_rec, val_sz, fd_funkier_alloc( funk, wksp ), wksp, opt_err );
251 0 : if( !buf ) {
252 0 : fd_funkier_rec_cancel( prepare );
253 0 : return NULL;
254 0 : }
255 0 : memcpy( buf, fd_funkier_val( old_rec, wksp ), val_sz );
256 :
257 0 : if( fd_funkier_rec_query_test( query ) ) {
258 0 : return new_rec;
259 0 : }
260 0 : }
261 0 : }
262 :
263 :
264 :
265 : int
266 0 : fd_funkier_rec_is_full( fd_funkier_t * funk ) {
267 0 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
268 0 : fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( funk, wksp );
269 0 : return fd_funkier_rec_pool_is_empty( &rec_pool );
270 0 : }
271 :
272 : int
273 : fd_funkier_rec_remove( fd_funkier_t * funk,
274 : fd_funkier_txn_t * txn,
275 : fd_funkier_rec_key_t const * key,
276 : fd_funkier_rec_t ** rec_out,
277 0 : ulong erase_data ) {
278 : #ifdef FD_FUNKIER_HANDHOLDING
279 : if( FD_UNLIKELY( funk==NULL || key==NULL ) ) {
280 : return FD_FUNKIER_ERR_INVAL;
281 : }
282 : if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
283 : return FD_FUNKIER_ERR_INVAL;
284 : }
285 : #endif
286 :
287 0 : if( !txn ) { /* Modifying last published */
288 0 : if( FD_UNLIKELY( fd_funkier_last_publish_is_frozen( funk ) ) ) {
289 0 : return FD_FUNKIER_ERR_FROZEN;
290 0 : }
291 0 : } else {
292 0 : if( FD_UNLIKELY( fd_funkier_txn_is_frozen( txn ) ) ) {
293 0 : return FD_FUNKIER_ERR_FROZEN;
294 0 : }
295 0 : }
296 :
297 0 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
298 0 : fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
299 0 : fd_funkier_xid_key_pair_t pair[1];
300 0 : if( txn == NULL ) {
301 0 : fd_funkier_txn_xid_set_root( pair->xid );
302 0 : } else {
303 0 : fd_funkier_txn_xid_copy( pair->xid, &txn->xid );
304 0 : }
305 0 : fd_funkier_rec_key_copy( pair->key, key );
306 0 : fd_funkier_rec_query_t query[ 1 ];
307 0 : for(;;) {
308 0 : int err = fd_funkier_rec_map_query_try( &rec_map, pair, NULL, query );
309 0 : if( err == FD_MAP_SUCCESS ) break;
310 0 : if( err == FD_MAP_ERR_KEY ) return FD_FUNKIER_SUCCESS;
311 0 : if( err == FD_MAP_ERR_AGAIN ) continue;
312 0 : FD_LOG_CRIT(( "query returned err %d", err ));
313 0 : }
314 :
315 0 : fd_funkier_rec_t * rec = fd_funkier_rec_map_query_ele( query );
316 0 : if( rec_out ) *rec_out = rec;
317 :
318 : /* Access the flags atomically */
319 0 : ulong old_flags;
320 0 : for(;;) {
321 0 : old_flags = rec->flags;
322 0 : if( FD_UNLIKELY( old_flags & FD_FUNKIER_REC_FLAG_ERASE ) ) return FD_FUNKIER_SUCCESS;
323 0 : if( FD_ATOMIC_CAS( &rec->flags, old_flags, old_flags | FD_FUNKIER_REC_FLAG_ERASE ) == old_flags ) break;
324 0 : }
325 :
326 : /* Flush the value and leave a tombstone behind. In theory, this can
327 : lead to an unbounded number of records, but for application
328 : reasons, we need to remember what was deleted. */
329 :
330 0 : fd_funkier_val_flush( rec, fd_funkier_alloc( funk, wksp ), wksp );
331 :
332 : /* At this point, the 5 most significant bytes should store data about the
333 : transaction that the record was updated in. */
334 :
335 0 : fd_funkier_rec_set_erase_data( rec, erase_data );
336 :
337 0 : return FD_FUNKIER_SUCCESS;
338 0 : }
339 :
340 : void
341 0 : fd_funkier_rec_set_erase_data( fd_funkier_rec_t * rec, ulong erase_data ) {
342 0 : rec->flags |= ((erase_data & 0xFFFFFFFFFFUL) << (sizeof(unsigned long) * 8 - 40));
343 0 : }
344 :
345 : ulong
346 0 : fd_funkier_rec_get_erase_data( fd_funkier_rec_t const * rec ) {
347 0 : return (rec->flags >> (sizeof(unsigned long) * 8 - 40)) & 0xFFFFFFFFFFUL;
348 0 : }
349 :
350 : static void
351 0 : fd_funkier_all_iter_skip_nulls( fd_funkier_all_iter_t * iter ) {
352 0 : if( iter->chain_idx == iter->chain_cnt ) return;
353 0 : while( fd_funkier_rec_map_iter_done( iter->rec_map_iter ) ) {
354 0 : if( ++(iter->chain_idx) == iter->chain_cnt ) break;
355 0 : iter->rec_map_iter = fd_funkier_rec_map_iter( &iter->rec_map, iter->chain_idx );
356 0 : }
357 0 : }
358 :
359 : void
360 0 : fd_funkier_all_iter_new( fd_funkier_t * funk, fd_funkier_all_iter_t * iter ) {
361 0 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
362 0 : iter->rec_map = fd_funkier_rec_map( funk, wksp );
363 0 : iter->chain_cnt = fd_funkier_rec_map_chain_cnt( &iter->rec_map );
364 0 : iter->chain_idx = 0;
365 0 : iter->rec_map_iter = fd_funkier_rec_map_iter( &iter->rec_map, 0 );
366 0 : fd_funkier_all_iter_skip_nulls( iter );
367 0 : }
368 :
369 : int
370 0 : fd_funkier_all_iter_done( fd_funkier_all_iter_t * iter ) {
371 0 : return ( iter->chain_idx == iter->chain_cnt );
372 0 : }
373 :
374 : void
375 0 : fd_funkier_all_iter_next( fd_funkier_all_iter_t * iter ) {
376 0 : iter->rec_map_iter = fd_funkier_rec_map_iter_next( iter->rec_map_iter );
377 0 : fd_funkier_all_iter_skip_nulls( iter );
378 0 : }
379 :
380 : fd_funkier_rec_t const *
381 0 : fd_funkier_all_iter_ele_const( fd_funkier_all_iter_t * iter ) {
382 0 : return fd_funkier_rec_map_iter_ele_const( iter->rec_map_iter );
383 0 : }
384 :
385 : #ifdef FD_FUNKIER_HANDHOLDING
386 : int
387 : fd_funkier_rec_verify( fd_funkier_t * funk ) {
388 : fd_wksp_t * wksp = fd_funkier_wksp( funk ); /* Previously verified */
389 : fd_funkier_txn_map_t txn_map = fd_funkier_txn_map( funk, wksp ); /* Previously verified */
390 : fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp ); /* Previously verified */
391 : fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( funk, wksp ); /* Previously verified */
392 : fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( funk, wksp ); /* Previously verified */
393 : ulong txn_max = funk->txn_max; /* Previously verified */
394 : ulong rec_max = funk->rec_max; /* Previously verified */
395 :
396 : /* At this point, txn_map has been extensively verified */
397 :
398 : # define TEST(c) do { \
399 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNKIER_ERR_INVAL; } \
400 : } while(0)
401 :
402 : TEST( !fd_funkier_rec_map_verify( &rec_map ) );
403 : TEST( !fd_funkier_rec_pool_verify( &rec_pool ) );
404 :
405 : /* Iterate over all records in use */
406 :
407 : fd_funkier_all_iter_t iter[1];
408 : for( fd_funkier_all_iter_new( funk, iter ); !fd_funkier_all_iter_done( iter ); fd_funkier_all_iter_next( iter ) ) {
409 : fd_funkier_rec_t const * rec = fd_funkier_all_iter_ele_const( iter );
410 :
411 : /* Make sure every record either links up with the last published
412 : transaction or an in-prep transaction and the flags are sane. */
413 :
414 : fd_funkier_txn_xid_t const * txn_xid = fd_funkier_rec_xid( rec );
415 : ulong txn_idx = fd_funkier_txn_idx( rec->txn_cidx );
416 :
417 : if( fd_funkier_txn_idx_is_null( txn_idx ) ) { /* This is a record from the last published transaction */
418 :
419 : TEST( fd_funkier_txn_xid_eq_root( txn_xid ) );
420 :
421 : } else { /* This is a record from an in-prep transaction */
422 :
423 : TEST( txn_idx<txn_max );
424 : fd_funkier_txn_t const * txn = fd_funkier_txn_query( txn_xid, &txn_map );
425 : TEST( txn );
426 : TEST( txn==(txn_pool.ele+txn_idx) );
427 :
428 : }
429 : }
430 :
431 : /* Clear record tags and then verify the forward and reverse linkage */
432 :
433 : for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool.ele[ rec_idx ].tag = 0U;
434 :
435 : do {
436 : ulong txn_idx = FD_FUNKIER_TXN_IDX_NULL;
437 : ulong rec_idx = funk->rec_head_idx;
438 : while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
439 : TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==0U );
440 : rec_pool.ele[ rec_idx ].tag = 1U;
441 : fd_funkier_rec_query_t query[1];
442 : fd_funkier_rec_t const * rec2 = fd_funkier_rec_query_try_global( funk, NULL, rec_pool.ele[ rec_idx ].pair.key, NULL, query );
443 : if( FD_UNLIKELY( rec_pool.ele[ rec_idx ].flags & FD_FUNKIER_REC_FLAG_ERASE ) )
444 : TEST( rec2 == NULL );
445 : else
446 : TEST( rec2 = rec_pool.ele + rec_idx );
447 : ulong next_idx = rec_pool.ele[ rec_idx ].next_idx;
448 : if( !fd_funkier_rec_idx_is_null( next_idx ) ) TEST( rec_pool.ele[ next_idx ].prev_idx==rec_idx );
449 : rec_idx = next_idx;
450 : }
451 : fd_funkier_txn_all_iter_t txn_iter[1];
452 : for( fd_funkier_txn_all_iter_new( funk, txn_iter ); !fd_funkier_txn_all_iter_done( txn_iter ); fd_funkier_txn_all_iter_next( txn_iter ) ) {
453 : fd_funkier_txn_t const * txn = fd_funkier_txn_all_iter_ele_const( txn_iter );
454 :
455 : ulong txn_idx = (ulong)(txn-txn_pool.ele);
456 : ulong rec_idx = txn->rec_head_idx;
457 : while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
458 : TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==0U );
459 : rec_pool.ele[ rec_idx ].tag = 1U;
460 : fd_funkier_rec_query_t query[1];
461 : fd_funkier_rec_t const * rec2 = fd_funkier_rec_query_try_global( funk, txn, rec_pool.ele[ rec_idx ].pair.key, NULL, query );
462 : if( FD_UNLIKELY( rec_pool.ele[ rec_idx ].flags & FD_FUNKIER_REC_FLAG_ERASE ) )
463 : TEST( rec2 == NULL );
464 : else
465 : TEST( rec2 = rec_pool.ele + rec_idx );
466 : ulong next_idx = rec_pool.ele[ rec_idx ].next_idx;
467 : if( !fd_funkier_rec_idx_is_null( next_idx ) ) TEST( rec_pool.ele[ next_idx ].prev_idx==rec_idx );
468 : rec_idx = next_idx;
469 : }
470 : }
471 : } while(0);
472 :
473 : do {
474 : ulong txn_idx = FD_FUNKIER_TXN_IDX_NULL;
475 : ulong rec_idx = funk->rec_tail_idx;
476 : while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
477 : TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==1U );
478 : rec_pool.ele[ rec_idx ].tag = 2U;
479 : ulong prev_idx = rec_pool.ele[ rec_idx ].prev_idx;
480 : if( !fd_funkier_rec_idx_is_null( prev_idx ) ) TEST( rec_pool.ele[ prev_idx ].next_idx==rec_idx );
481 : rec_idx = prev_idx;
482 : }
483 :
484 : fd_funkier_txn_all_iter_t txn_iter[1];
485 : for( fd_funkier_txn_all_iter_new( funk, txn_iter ); !fd_funkier_txn_all_iter_done( txn_iter ); fd_funkier_txn_all_iter_next( txn_iter ) ) {
486 : fd_funkier_txn_t const * txn = fd_funkier_txn_all_iter_ele_const( txn_iter );
487 :
488 : ulong txn_idx = (ulong)(txn-txn_pool.ele);
489 : ulong rec_idx = txn->rec_tail_idx;
490 : while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
491 : TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==1U );
492 : rec_pool.ele[ rec_idx ].tag = 2U;
493 : ulong prev_idx = rec_pool.ele[ rec_idx ].prev_idx;
494 : if( !fd_funkier_rec_idx_is_null( prev_idx ) ) TEST( rec_pool.ele[ prev_idx ].next_idx==rec_idx );
495 : rec_idx = prev_idx;
496 : }
497 : }
498 : } while(0);
499 :
500 : # undef TEST
501 :
502 : return FD_FUNKIER_SUCCESS;
503 : }
504 : #endif
|