Line data Source code
1 : #include "fd_funk_private.h"
2 : #include "../util/racesan/fd_racesan_target.h"
3 :
4 : /* Provide the actual record map implementation */
5 :
6 : #define POOL_NAME fd_funk_rec_pool
7 2555865 : #define POOL_ELE_T fd_funk_rec_t
8 : #define POOL_IDX_T uint
9 2566191 : #define POOL_NEXT map_next
10 : #define POOL_IMPL_STYLE 2
11 : #define POOL_LAZY 1
12 : #include "../util/tmpl/fd_pool_para.c"
13 :
14 : #define MAP_NAME fd_funk_rec_map
15 1326336 : #define MAP_ELE_T fd_funk_rec_t
16 13302 : #define MAP_KEY_T fd_funk_xid_key_pair_t
17 1290390 : #define MAP_KEY pair
18 : #define MAP_KEY_EQ(k0,k1) fd_funk_xid_key_pair_eq((k0),(k1))
19 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
20 1325766 : #define MAP_IDX_T uint
21 3283680 : #define MAP_NEXT map_next
22 114 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
23 5108718 : #define MAP_CNT_WIDTH FD_FUNK_REC_MAP_CNT_WIDTH
24 : #define MAP_IMPL_STYLE 2
25 : #define MAP_PEDANTIC 1
26 : #include "../util/tmpl/fd_map_chain_para.c"
27 :
28 : static fd_funk_txn_t *
29 : fd_funk_rec_txn_borrow( fd_funk_t const * funk,
30 : fd_funk_txn_xid_t const * xid,
31 1281156 : fd_funk_txn_map_query_t * query ) {
32 1281156 : memset( query, 0, sizeof(fd_funk_txn_map_query_t) );
33 1281156 : if( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) return NULL;
34 :
35 1281150 : fd_funk_txn_map_query_t txn_query[1];
36 1281150 : for(;;) {
37 1281150 : int txn_query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, txn_query, 0 );
38 1281150 : if( FD_UNLIKELY( txn_query_err==FD_MAP_ERR_AGAIN ) ) continue;
39 1281150 : if( FD_UNLIKELY( txn_query_err!=FD_MAP_SUCCESS ) ) {
40 0 : FD_LOG_CRIT(( "fd_funk_rec op failed: txn_map_query_try(%lu:%lu) error %i-%s",
41 0 : xid->ul[0], xid->ul[1],
42 0 : txn_query_err, fd_map_strerror( txn_query_err ) ));
43 0 : }
44 1281150 : break;
45 1281150 : }
46 1281150 : fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( txn_query );
47 1281150 : uint txn_state = FD_VOLATILE_CONST( txn->state );
48 1281150 : if( FD_UNLIKELY( txn_state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
49 0 : FD_LOG_CRIT(( "fd_funk_rec op failed: txn %p %lu:%lu state is %u-%s",
50 0 : (void *)txn, xid->ul[0], xid->ul[1],
51 0 : txn_state, fd_funk_txn_state_str( txn_state ) ));
52 0 : }
53 1281150 : return txn;
54 1281150 : }
55 :
56 : static void
57 1281156 : fd_funk_rec_txn_release( fd_funk_txn_map_query_t const * query ) {
58 1281156 : if( !query->ele ) return;
59 0 : if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
60 0 : FD_LOG_CRIT(( "fd_funk_rec_txn_release: fd_funk_txn_map_query_test failed (data race detected?)" ));
61 0 : }
62 0 : }
63 :
64 : static void
65 : fd_funk_rec_key_set_pair( fd_funk_xid_key_pair_t * key_pair,
66 : fd_funk_txn_xid_t const * xid,
67 4188 : fd_funk_rec_key_t const * key ) {
68 4188 : fd_funk_txn_xid_copy( key_pair->xid, xid );
69 4188 : fd_funk_rec_key_copy( key_pair->key, key );
70 4188 : }
71 :
72 : fd_funk_rec_t *
73 : fd_funk_rec_query_try( fd_funk_t * funk,
74 : fd_funk_txn_xid_t const * xid,
75 : fd_funk_rec_key_t const * key,
76 1005 : fd_funk_rec_query_t * query ) {
77 1005 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
78 1005 : if( FD_UNLIKELY( !xid ) ) FD_LOG_CRIT(( "NULL xid" ));
79 1005 : if( FD_UNLIKELY( !key ) ) FD_LOG_CRIT(( "NULL key" ));
80 1005 : if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
81 :
82 1005 : fd_funk_xid_key_pair_t pair[1];
83 1005 : if( FD_UNLIKELY( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) ) {
84 0 : fd_funk_txn_xid_set_root( pair->xid );
85 1005 : } else {
86 1005 : fd_funk_txn_xid_copy( pair->xid, xid );
87 1005 : }
88 1005 : fd_funk_rec_key_copy( pair->key, key );
89 :
90 1005 : for(;;) {
91 1005 : int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
92 1005 : if( err == FD_MAP_SUCCESS ) break;
93 0 : if( err == FD_MAP_ERR_KEY ) return NULL;
94 0 : if( err == FD_MAP_ERR_AGAIN ) continue;
95 0 : FD_LOG_CRIT(( "query returned err %d", err ));
96 0 : }
97 1005 : return fd_funk_rec_map_query_ele( query );
98 1005 : }
99 :
100 : fd_funk_rec_t const *
101 : fd_funk_rec_query_try_global( fd_funk_t const * funk,
102 : fd_funk_txn_xid_t const * xid,
103 : fd_funk_rec_key_t const * key,
104 : fd_funk_txn_xid_t * txn_out,
105 4188 : fd_funk_rec_query_t * query ) {
106 4188 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
107 4188 : if( FD_UNLIKELY( !key ) ) FD_LOG_CRIT(( "NULL key" ));
108 4188 : if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
109 :
110 : /* Attach to the funk transaction */
111 :
112 4188 : fd_funk_txn_map_query_t txn_query[1];
113 4188 : fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
114 :
115 : /* Look for the first element in the hash chain with the right
116 : record key. This takes advantage of the fact that elements with
117 : the same record key appear on the same hash chain in order of
118 : newest to oldest. */
119 :
120 4188 : fd_funk_xid_key_pair_t pair[1];
121 4188 : fd_funk_rec_key_set_pair( pair, xid, key );
122 :
123 4188 : fd_funk_rec_map_t const * rec_map = funk->rec_map;
124 :
125 4188 : ulong hash = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
126 4188 : ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
127 4188 : fd_funk_rec_map_shmem_private_chain_t * chain = fd_funk_rec_map_shmem_private_chain( rec_map->map, hash );
128 :
129 : /* Start a speculative record map transaction */
130 :
131 4188 : struct {
132 4188 : fd_funk_rec_map_txn_t txn;
133 4188 : fd_funk_rec_map_txn_private_info_t lock[1];
134 4188 : } txn_mem;
135 4188 : fd_funk_rec_map_txn_t * rec_txn;
136 4188 : fd_funk_rec_t const * res;
137 :
138 4188 : retry:
139 4188 : res = NULL;
140 4188 : rec_txn = fd_funk_rec_map_txn_init( &txn_mem.txn, (void *)rec_map, 1UL );
141 4188 : txn_mem.lock[ 0 ].chain = chain;
142 4188 : txn_mem.txn.spec_cnt++;
143 4188 : if( FD_UNLIKELY( fd_funk_rec_map_txn_try( rec_txn, FD_MAP_FLAG_BLOCKING )!=FD_MAP_SUCCESS ) ) {
144 0 : FD_LOG_CRIT(( "fd_funk_rec_map_txn_try failed" ));
145 0 : }
146 :
147 4188 : query->ele = NULL;
148 4188 : query->chain = chain;
149 4188 : query->ver_cnt = txn_mem.lock[0].ver_cnt;
150 :
151 4188 : for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( funk->rec_map, chain_idx );
152 4191 : !fd_funk_rec_map_iter_done( iter );
153 4188 : iter = fd_funk_rec_map_iter_next( iter ) ) {
154 516 : fd_funk_rec_t const * ele = fd_funk_rec_map_iter_ele_const( iter );
155 516 : if( FD_LIKELY( fd_funk_rec_key_eq( key, ele->pair.key ) ) ) {
156 :
157 : /* For cur_txn in path from [txn] to [root] where root is NULL */
158 :
159 540 : for( fd_funk_txn_t const * cur_txn = txn; ; cur_txn = fd_funk_txn_parent( cur_txn, funk->txn_pool ) ) {
160 : /* If record ele is part of transaction cur_txn, we have a
161 : match. According to the property above, this will be the
162 : youngest descendent in the transaction stack. */
163 :
164 540 : int match = FD_UNLIKELY( cur_txn ) ? /* opt for root find (FIXME: eliminate branch with cmov into txn_xid_eq?) */
165 528 : fd_funk_txn_xid_eq( &cur_txn->xid, ele->pair.xid ) :
166 540 : fd_funk_txn_xid_eq_root( ele->pair.xid );
167 :
168 540 : if( FD_LIKELY( match ) ) {
169 513 : if( txn_out ) {
170 48 : *txn_out = *( cur_txn ? &cur_txn->xid : fd_funk_last_publish( funk ) );
171 48 : }
172 513 : query->ele = (fd_funk_rec_t *)ele;
173 513 : res = query->ele;
174 513 : goto found;
175 513 : }
176 :
177 27 : if( cur_txn == NULL ) break;
178 27 : }
179 516 : }
180 516 : }
181 :
182 4188 : found:
183 4188 : if( FD_UNLIKELY( fd_funk_rec_map_txn_test( rec_txn )!=FD_MAP_SUCCESS ) ) goto retry;
184 4188 : if( FD_LIKELY( txn ) ) fd_funk_txn_xid_assert( txn, pair->xid );
185 4188 : fd_funk_rec_txn_release( txn_query );
186 4188 : return res;
187 4188 : }
188 :
189 : fd_funk_rec_t *
190 : fd_funk_rec_prepare( fd_funk_t * funk,
191 : fd_funk_txn_xid_t const * xid,
192 : fd_funk_rec_key_t const * key,
193 : fd_funk_rec_prepare_t * prepare,
194 1276968 : int * opt_err ) {
195 1276968 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
196 1276968 : if( FD_UNLIKELY( !xid ) ) FD_LOG_CRIT(( "NULL xid" ));
197 1276968 : if( FD_UNLIKELY( !prepare ) ) FD_LOG_CRIT(( "NULL prepare" ));
198 :
199 1276968 : fd_funk_txn_map_query_t txn_query[1];
200 1276968 : fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
201 :
202 1276968 : memset( prepare, 0, sizeof(fd_funk_rec_prepare_t) );
203 :
204 1276968 : if( !txn ) { /* Modifying last published */
205 0 : if( FD_UNLIKELY( fd_funk_last_publish_is_frozen( funk ) ) ) {
206 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
207 0 : fd_funk_rec_txn_release( txn_query );
208 0 : return NULL;
209 0 : }
210 1276968 : } else {
211 1276968 : if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
212 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
213 0 : fd_funk_rec_txn_release( txn_query );
214 0 : return NULL;
215 0 : }
216 1276968 : }
217 :
218 1276968 : fd_funk_rec_t * rec = prepare->rec = fd_funk_rec_pool_acquire( funk->rec_pool, NULL, 1, opt_err );
219 1276968 : if( opt_err && *opt_err == FD_POOL_ERR_CORRUPT ) {
220 0 : FD_LOG_CRIT(( "corrupt element returned from funk rec pool" ));
221 0 : }
222 1276968 : if( FD_UNLIKELY( !rec ) ) {
223 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_REC );
224 0 : fd_funk_rec_txn_release( txn_query );
225 0 : return rec;
226 0 : }
227 :
228 1276968 : fd_funk_val_init( rec );
229 1276968 : if( txn == NULL ) {
230 0 : fd_funk_txn_xid_set_root( rec->pair.xid );
231 1276968 : } else {
232 1276968 : fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
233 1276968 : prepare->rec_head_idx = &txn->rec_head_idx;
234 1276968 : prepare->rec_tail_idx = &txn->rec_tail_idx;
235 1276968 : }
236 1276968 : fd_funk_rec_key_copy( rec->pair.key, key );
237 1276968 : rec->tag = 0;
238 1276968 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
239 1276968 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
240 1276968 : fd_funk_rec_txn_release( txn_query );
241 1276968 : return rec;
242 1276968 : }
243 :
244 : #if FD_HAS_ATOMIC
245 :
246 : static void
247 : fd_funk_rec_push_tail( fd_funk_t * funk,
248 1277013 : fd_funk_rec_prepare_t * prepare ) {
249 1277013 : fd_funk_rec_t * rec = prepare->rec;
250 1277013 : uint rec_idx = (uint)( rec - funk->rec_pool->ele );
251 1277013 : uint * rec_head_idx = prepare->rec_head_idx;
252 1277013 : uint * rec_tail_idx = prepare->rec_tail_idx;
253 :
254 1277013 : for(;;) {
255 :
256 : /* Doubly linked list append. Robust in the event of concurrent
257 : publishes. Iteration during publish not supported. Sequence:
258 : - Identify tail element
259 : - Set new element's prev and next pointers
260 : - Set tail element's next pointer
261 : - Set tail pointer */
262 :
263 1277013 : uint rec_prev_idx = FD_VOLATILE_CONST( *rec_tail_idx );
264 1277013 : rec->prev_idx = rec_prev_idx;
265 1277013 : FD_COMPILER_MFENCE();
266 :
267 1277013 : uint * next_idx_p;
268 1277013 : if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
269 395886 : next_idx_p = rec_head_idx;
270 881127 : } else {
271 881127 : next_idx_p = &funk->rec_pool->ele[ rec_prev_idx ].next_idx;
272 881127 : }
273 :
274 1277013 : fd_racesan_hook( "funk_rec_push_tail:next_cas" );
275 1277013 : if( FD_UNLIKELY( !__sync_bool_compare_and_swap( next_idx_p, FD_FUNK_REC_IDX_NULL, rec_idx ) ) ) {
276 : /* Another thread beat us to the punch */
277 0 : FD_SPIN_PAUSE();
278 0 : continue;
279 0 : }
280 :
281 1277013 : fd_racesan_hook( "funk_rec_push_tail:tail_cas" );
282 1277013 : if( FD_UNLIKELY( !__sync_bool_compare_and_swap( rec_tail_idx, rec_prev_idx, rec_idx ) ) ) {
283 : /* This CAS is guaranteed to succeed if the previous CAS passed. */
284 0 : FD_LOG_CRIT(( "Irrecoverable data race encountered while appending to txn rec list (invariant violation?): cas(%p,%u,%u)",
285 0 : (void *)rec_tail_idx, rec_prev_idx, rec_idx ));
286 0 : }
287 :
288 1277013 : break;
289 1277013 : }
290 1277013 : }
291 :
292 : #else
293 :
294 : static void
295 : fd_funk_rec_push_tail( fd_funk_t * funk,
296 : fd_funk_rec_prepare_t * prepare ) {
297 : fd_funk_rec_t * rec = prepare->rec;
298 : uint rec_idx = (uint)( rec - funk->rec_pool->ele );
299 : uint * rec_head_idx = prepare->rec_head_idx;
300 : uint * rec_tail_idx = prepare->rec_tail_idx;
301 : uint rec_prev_idx = *rec_tail_idx;
302 : *rec_tail_idx = rec_idx;
303 : rec->prev_idx = rec_prev_idx;
304 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
305 : if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
306 : *rec_head_idx = rec_idx;
307 : } else {
308 : funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
309 : }
310 : }
311 :
312 : #endif
313 :
314 : void
315 : fd_funk_rec_publish( fd_funk_t * funk,
316 1277013 : fd_funk_rec_prepare_t * prepare ) {
317 1277013 : fd_funk_rec_t * rec = prepare->rec;
318 1277013 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
319 1277013 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
320 :
321 1277013 : if( prepare->rec_head_idx ) {
322 1277013 : fd_funk_rec_push_tail( funk, prepare );
323 1277013 : }
324 :
325 1277013 : fd_racesan_hook( "funk_rec_publish:map_insert" );
326 1277013 : int insert_err = fd_funk_rec_map_insert( funk->rec_map, rec, FD_MAP_FLAG_BLOCKING );
327 1277013 : if( insert_err ) {
328 0 : FD_LOG_CRIT(( "fd_funk_rec_map_insert failed (%i-%s)", insert_err, fd_map_strerror( insert_err ) ));
329 0 : }
330 1277013 : }
331 :
332 : int
333 252 : fd_funk_rec_verify( fd_funk_t * funk ) {
334 252 : fd_funk_rec_map_t * rec_map = funk->rec_map;
335 252 : fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
336 252 : ulong rec_max = fd_funk_rec_pool_ele_max( rec_pool );
337 :
338 892848 : # define TEST(c) do { \
339 892848 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
340 892848 : } while(0)
341 :
342 252 : TEST( !fd_funk_rec_map_verify( rec_map ) );
343 252 : TEST( !fd_funk_rec_pool_verify( rec_pool ) );
344 :
345 : /* Iterate (again) over all records in use */
346 :
347 252 : fd_funk_rec_map_shmem_private_chain_t * chain_tbl =
348 252 : fd_funk_rec_map_shmem_private_chain( rec_map->map, 0UL );
349 252 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
350 838050 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
351 837798 : ulong chain_ele_cnt = fd_funk_rec_map_private_vcnt_cnt( chain_tbl[ chain_idx ].ver_cnt );
352 837798 : ulong chain_ele_idx = 0UL;
353 837798 : for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
354 851100 : !fd_funk_rec_map_iter_done( iter );
355 837798 : iter = fd_funk_rec_map_iter_next( iter ), chain_ele_idx++ ) {
356 13302 : fd_funk_rec_t const * rec = fd_funk_rec_map_iter_ele_const( iter );
357 :
358 : /* Make sure every record either links up with the last published
359 : transaction or an in-prep transaction and the flags are sane. */
360 :
361 13302 : fd_funk_txn_xid_t const * xid = fd_funk_rec_xid( rec );
362 :
363 13302 : if( fd_funk_txn_xid_eq_root( xid ) ) { /* This is a record from the last published transaction */
364 :
365 12297 : TEST( fd_funk_txn_xid_eq_root( xid ) );
366 : /* No record linked list at the root txn */
367 12297 : TEST( fd_funk_rec_idx_is_null( rec->prev_idx ) );
368 12297 : TEST( fd_funk_rec_idx_is_null( rec->next_idx ) );
369 :
370 12297 : } else { /* This is a record from an in-prep transaction */
371 :
372 1005 : TEST( fd_funk_txn_query( xid, funk->txn_map ) );
373 :
374 1005 : }
375 13302 : }
376 837798 : TEST( chain_ele_idx==chain_ele_cnt );
377 837798 : }
378 :
379 : /* Clear record tags and then verify membership */
380 :
381 1675836 : for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool->ele[ rec_idx ].tag = 0;
382 :
383 252 : do {
384 252 : fd_funk_all_iter_t iter[1];
385 13554 : for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
386 13302 : fd_funk_rec_t * rec = fd_funk_all_iter_ele( iter );
387 13302 : if( fd_funk_txn_xid_eq_root( rec->pair.xid ) ) {
388 12297 : TEST( !rec->tag );
389 12297 : rec->tag = 1;
390 12297 : }
391 13302 : }
392 :
393 252 : fd_funk_txn_all_iter_t txn_iter[1];
394 843 : for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
395 591 : fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
396 :
397 591 : uint rec_idx = txn->rec_head_idx;
398 1596 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
399 1005 : TEST( (rec_idx<rec_max) && !rec_pool->ele[ rec_idx ].tag );
400 1005 : rec_pool->ele[ rec_idx ].tag = 1;
401 1005 : fd_funk_rec_query_t query[1];
402 1005 : fd_funk_rec_t const * rec2 = fd_funk_rec_query_try( funk, &txn->xid, rec_pool->ele[ rec_idx ].pair.key, query );
403 1005 : TEST( rec2 == rec_pool->ele + rec_idx );
404 1005 : uint next_idx = rec_pool->ele[ rec_idx ].next_idx;
405 1005 : if( !fd_funk_rec_idx_is_null( next_idx ) ) TEST( rec_pool->ele[ next_idx ].prev_idx==rec_idx );
406 1005 : rec_idx = next_idx;
407 1005 : }
408 591 : }
409 252 : } while(0);
410 :
411 252 : do {
412 252 : fd_funk_txn_all_iter_t txn_iter[1];
413 843 : for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
414 591 : fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
415 :
416 591 : uint rec_idx = txn->rec_tail_idx;
417 1596 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
418 1005 : TEST( (rec_idx<rec_max) && rec_pool->ele[ rec_idx ].tag );
419 1005 : rec_pool->ele[ rec_idx ].tag = 0;
420 1005 : uint prev_idx = rec_pool->ele[ rec_idx ].prev_idx;
421 1005 : if( !fd_funk_rec_idx_is_null( prev_idx ) ) TEST( rec_pool->ele[ prev_idx ].next_idx==rec_idx );
422 1005 : rec_idx = prev_idx;
423 1005 : }
424 591 : }
425 252 : } while(0);
426 :
427 252 : fd_funk_all_iter_t iter[1];
428 13554 : for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
429 13302 : fd_funk_rec_t const * rec = fd_funk_all_iter_ele( iter );
430 13302 : FD_TEST( rec->tag == fd_funk_txn_xid_eq_root( rec->pair.xid ) );
431 13302 : }
432 :
433 252 : # undef TEST
434 :
435 252 : return FD_FUNK_SUCCESS;
436 252 : }
|