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 4358223 : #define POOL_ELE_T fd_funk_rec_t
8 : #define POOL_IDX_T uint
9 7709352 : #define POOL_NEXT map_next
10 : #define POOL_IMPL_STYLE 2
11 : #include "../util/tmpl/fd_pool_para.c"
12 :
13 : #define MAP_NAME fd_funk_rec_map
14 639994143 : #define MAP_ELE_T fd_funk_rec_t
15 0 : #define MAP_KEY_T fd_funk_xid_key_pair_t
16 2178999 : #define MAP_KEY pair
17 : #define MAP_KEY_EQ(k0,k1) fd_funk_xid_key_pair_eq((k0),(k1))
18 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
19 639994065 : #define MAP_IDX_T uint
20 772588977 : #define MAP_NEXT map_next
21 42 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
22 : #define MAP_IMPL_STYLE 2
23 : #include "../util/tmpl/fd_map_chain_para.c"
24 :
25 : static fd_funk_txn_t *
26 : fd_funk_rec_txn_borrow( fd_funk_t const * funk,
27 : fd_funk_txn_xid_t const * xid,
28 61586682 : fd_funk_txn_map_query_t * query ) {
29 61586682 : memset( query, 0, sizeof(fd_funk_txn_map_query_t) );
30 61586682 : if( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) return NULL;
31 :
32 50652795 : fd_funk_txn_map_query_t txn_query[1];
33 50652795 : for(;;) {
34 50652795 : int txn_query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, txn_query, 0 );
35 50652795 : if( FD_UNLIKELY( txn_query_err==FD_MAP_ERR_AGAIN ) ) continue;
36 50652795 : if( FD_UNLIKELY( txn_query_err!=FD_MAP_SUCCESS ) ) {
37 0 : FD_LOG_CRIT(( "fd_funk_rec op failed: txn_map_query_try(%lu:%lu) error %i-%s",
38 0 : xid->ul[0], xid->ul[1],
39 0 : txn_query_err, fd_map_strerror( txn_query_err ) ));
40 0 : }
41 50652795 : break;
42 50652795 : }
43 50652795 : fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( txn_query );
44 50652795 : uint txn_state = FD_VOLATILE_CONST( txn->state );
45 50652795 : if( FD_UNLIKELY( txn_state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
46 0 : FD_LOG_CRIT(( "fd_funk_rec op failed: txn %p %lu:%lu state is %u-%s",
47 0 : (void *)txn, xid->ul[0], xid->ul[1],
48 0 : txn_state, fd_funk_txn_state_str( txn_state ) ));
49 0 : }
50 50652795 : return txn;
51 50652795 : }
52 :
53 : static void
54 61586682 : fd_funk_rec_txn_release( fd_funk_txn_map_query_t const * query ) {
55 61586682 : if( !query->ele ) return;
56 0 : if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
57 0 : FD_LOG_CRIT(( "fd_funk_rec_txn_release: fd_funk_txn_map_query_test failed (data race detected?)" ));
58 0 : }
59 0 : }
60 :
61 : static void
62 : fd_funk_rec_key_set_pair( fd_funk_xid_key_pair_t * key_pair,
63 : fd_funk_txn_xid_t const * xid,
64 59407638 : fd_funk_rec_key_t const * key ) {
65 59407638 : fd_funk_txn_xid_copy( key_pair->xid, xid );
66 59407638 : fd_funk_rec_key_copy( key_pair->key, key );
67 59407638 : }
68 :
69 : fd_funk_rec_t const *
70 : fd_funk_rec_query_try( fd_funk_t * funk,
71 : fd_funk_txn_xid_t const * xid,
72 : fd_funk_rec_key_t const * key,
73 637764174 : fd_funk_rec_query_t * query ) {
74 637764174 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
75 637764174 : if( FD_UNLIKELY( !xid ) ) FD_LOG_CRIT(( "NULL xid" ));
76 637764174 : if( FD_UNLIKELY( !key ) ) FD_LOG_CRIT(( "NULL key" ));
77 637764174 : if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
78 :
79 637764174 : fd_funk_xid_key_pair_t pair[1];
80 637764174 : if( FD_UNLIKELY( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) ) {
81 58809 : fd_funk_txn_xid_set_root( pair->xid );
82 637705365 : } else {
83 637705365 : fd_funk_txn_xid_copy( pair->xid, xid );
84 637705365 : }
85 637764174 : fd_funk_rec_key_copy( pair->key, key );
86 :
87 637764174 : for(;;) {
88 637764174 : int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
89 637764174 : if( err == FD_MAP_SUCCESS ) break;
90 45 : if( err == FD_MAP_ERR_KEY ) return NULL;
91 0 : if( err == FD_MAP_ERR_AGAIN ) continue;
92 0 : FD_LOG_CRIT(( "query returned err %d", err ));
93 0 : }
94 637764129 : return fd_funk_rec_map_query_ele_const( query );
95 637764174 : }
96 :
97 :
98 : fd_funk_rec_t *
99 : fd_funk_rec_modify( fd_funk_t * funk,
100 : fd_funk_txn_xid_t const * xid,
101 : fd_funk_rec_key_t const * key,
102 0 : fd_funk_rec_query_t * query ) {
103 0 : fd_funk_rec_map_t * rec_map = fd_funk_rec_map( funk );
104 0 : fd_funk_xid_key_pair_t pair[1];
105 0 : fd_funk_rec_key_set_pair( pair, xid, key );
106 :
107 0 : int err = fd_funk_rec_map_modify_try( rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
108 0 : if( err==FD_MAP_ERR_KEY ) return NULL;
109 0 : if( err!=FD_MAP_SUCCESS ) FD_LOG_CRIT(( "query returned err %d", err ));
110 :
111 0 : fd_funk_rec_t * rec = fd_funk_rec_map_query_ele( query );
112 0 : return rec;
113 0 : }
114 :
115 : void
116 0 : fd_funk_rec_modify_publish( fd_funk_rec_query_t * query ) {
117 0 : fd_funk_rec_map_modify_test( query );
118 0 : }
119 :
120 : fd_funk_rec_t const *
121 : fd_funk_rec_query_try_global( fd_funk_t const * funk,
122 : fd_funk_txn_xid_t const * xid,
123 : fd_funk_rec_key_t const * key,
124 : fd_funk_txn_xid_t * txn_out,
125 59407638 : fd_funk_rec_query_t * query ) {
126 59407638 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
127 59407638 : if( FD_UNLIKELY( !key ) ) FD_LOG_CRIT(( "NULL key" ));
128 59407638 : if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
129 :
130 : /* Attach to the funk transaction */
131 :
132 59407638 : fd_funk_txn_map_query_t txn_query[1];
133 59407638 : fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
134 :
135 : /* Look for the first element in the hash chain with the right
136 : record key. This takes advantage of the fact that elements with
137 : the same record key appear on the same hash chain in order of
138 : newest to oldest. */
139 :
140 59407638 : fd_funk_xid_key_pair_t pair[1];
141 59407638 : fd_funk_rec_key_set_pair( pair, xid, key );
142 :
143 59407638 : fd_funk_rec_map_t const * rec_map = funk->rec_map;
144 :
145 59407638 : ulong hash = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
146 59407638 : ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
147 59407638 : fd_funk_rec_map_shmem_private_chain_t * chain = fd_funk_rec_map_shmem_private_chain( rec_map->map, hash );
148 :
149 : /* Start a speculative record map transaction */
150 :
151 59407638 : struct {
152 59407638 : fd_funk_rec_map_txn_t txn;
153 59407638 : fd_funk_rec_map_txn_private_info_t lock[1];
154 59407638 : } txn_mem;
155 59407638 : fd_funk_rec_map_txn_t * rec_txn;
156 59407638 : fd_funk_rec_t const * res;
157 :
158 59407638 : retry:
159 59407638 : res = NULL;
160 59407638 : rec_txn = fd_funk_rec_map_txn_init( &txn_mem.txn, (void *)rec_map, 1UL );
161 59407638 : txn_mem.lock[ 0 ].chain = chain;
162 59407638 : txn_mem.txn.spec_cnt++;
163 59407638 : if( FD_UNLIKELY( fd_funk_rec_map_txn_try( rec_txn, FD_MAP_FLAG_BLOCKING )!=FD_MAP_SUCCESS ) ) {
164 0 : FD_LOG_CRIT(( "fd_funk_rec_map_txn_try failed" ));
165 0 : }
166 :
167 59407638 : query->ele = NULL;
168 59407638 : query->chain = chain;
169 59407638 : query->ver_cnt = txn_mem.lock[0].ver_cnt;
170 :
171 59407638 : for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( funk->rec_map, chain_idx );
172 145188219 : !fd_funk_rec_map_iter_done( iter );
173 145152618 : iter = fd_funk_rec_map_iter_next( iter ) ) {
174 145152618 : fd_funk_rec_t const * ele = fd_funk_rec_map_iter_ele_const( iter );
175 145152618 : if( FD_LIKELY( fd_funk_rec_key_eq( key, ele->pair.key ) ) ) {
176 :
177 : /* For cur_txn in path from [txn] to [root] where root is NULL */
178 :
179 289088853 : for( fd_funk_txn_t const * cur_txn = txn; ; cur_txn = fd_funk_txn_parent( cur_txn, funk->txn_pool ) ) {
180 : /* If record ele is part of transaction cur_txn, we have a
181 : match. According to the property above, this will be the
182 : youngest descendent in the transaction stack. */
183 :
184 289088853 : int match = FD_UNLIKELY( cur_txn ) ? /* opt for root find (FIXME: eliminate branch with cmov into txn_xid_eq?) */
185 202187634 : fd_funk_txn_xid_eq( &cur_txn->xid, ele->pair.xid ) :
186 289088853 : fd_funk_txn_xid_eq_root( ele->pair.xid );
187 :
188 289088853 : if( FD_LIKELY( match ) ) {
189 59372037 : if( txn_out ) {
190 0 : *txn_out = *( cur_txn ? &cur_txn->xid : fd_funk_last_publish( funk ) );
191 0 : }
192 59372037 : query->ele = (fd_funk_rec_t *)ele;
193 59372037 : res = query->ele;
194 59372037 : goto found;
195 59372037 : }
196 :
197 229716816 : if( cur_txn == NULL ) break;
198 229716816 : }
199 98418258 : }
200 145152618 : }
201 :
202 59407638 : found:
203 59407638 : if( FD_UNLIKELY( fd_funk_rec_map_txn_test( rec_txn )!=FD_MAP_SUCCESS ) ) goto retry;
204 59407638 : if( FD_LIKELY( txn ) ) fd_funk_txn_xid_assert( txn, pair->xid );
205 59407638 : fd_funk_rec_txn_release( txn_query );
206 59407638 : return res;
207 59407638 : }
208 :
209 : fd_funk_rec_t const *
210 : fd_funk_rec_query_copy( fd_funk_t * funk,
211 : fd_funk_txn_xid_t const * xid,
212 : fd_funk_rec_key_t const * key,
213 : fd_valloc_t valloc,
214 0 : ulong * sz_out ) {
215 0 : *sz_out = ULONG_MAX;
216 0 : fd_funk_xid_key_pair_t pair[1];
217 0 : fd_funk_rec_key_set_pair( pair, xid, key );
218 :
219 0 : void * last_copy = NULL;
220 0 : ulong last_copy_sz = 0;
221 0 : for(;;) {
222 0 : fd_funk_rec_query_t query[1];
223 0 : int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
224 0 : if( err == FD_MAP_ERR_KEY ) {
225 0 : if( last_copy ) fd_valloc_free( valloc, last_copy );
226 0 : return NULL;
227 0 : }
228 0 : if( err == FD_MAP_ERR_AGAIN ) continue;
229 0 : if( err != FD_MAP_SUCCESS ) FD_LOG_CRIT(( "query returned err %d", err ));
230 0 : fd_funk_rec_t const * rec = fd_funk_rec_map_query_ele_const( query );
231 0 : ulong sz = fd_funk_val_sz( rec );
232 0 : void * copy;
233 0 : if( sz <= last_copy_sz ) {
234 0 : copy = last_copy;
235 0 : } else {
236 0 : if( last_copy ) fd_valloc_free( valloc, last_copy );
237 0 : copy = last_copy = fd_valloc_malloc( valloc, 1, sz );
238 0 : last_copy_sz = sz;
239 0 : }
240 0 : memcpy( copy, fd_funk_val( rec, fd_funk_wksp( funk ) ), sz );
241 0 : *sz_out = sz;
242 0 : if( !fd_funk_rec_query_test( query ) ) return copy;
243 0 : }
244 0 : }
245 :
246 : int
247 697167543 : fd_funk_rec_query_test( fd_funk_rec_query_t * query ) {
248 697167543 : return fd_funk_rec_map_query_test( query );
249 697167543 : }
250 :
251 : fd_funk_rec_t *
252 : fd_funk_rec_prepare( fd_funk_t * funk,
253 : fd_funk_txn_xid_t const * xid,
254 : fd_funk_rec_key_t const * key,
255 : fd_funk_rec_prepare_t * prepare,
256 2179044 : int * opt_err ) {
257 2179044 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
258 2179044 : if( FD_UNLIKELY( !xid ) ) FD_LOG_CRIT(( "NULL xid" ));
259 2179044 : if( FD_UNLIKELY( !prepare ) ) FD_LOG_CRIT(( "NULL prepare" ));
260 :
261 2179044 : fd_funk_txn_map_query_t txn_query[1];
262 2179044 : fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
263 :
264 2179044 : memset( prepare, 0, sizeof(fd_funk_rec_prepare_t) );
265 :
266 2179044 : if( !txn ) { /* Modifying last published */
267 306 : if( FD_UNLIKELY( fd_funk_last_publish_is_frozen( funk ) ) ) {
268 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
269 0 : fd_funk_rec_txn_release( txn_query );
270 0 : return NULL;
271 0 : }
272 2178738 : } else {
273 2178738 : if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
274 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
275 0 : fd_funk_rec_txn_release( txn_query );
276 0 : return NULL;
277 0 : }
278 2178738 : }
279 :
280 2179044 : fd_funk_rec_t * rec = prepare->rec = fd_funk_rec_pool_acquire( funk->rec_pool, NULL, 1, opt_err );
281 2179044 : if( opt_err && *opt_err == FD_POOL_ERR_CORRUPT ) {
282 0 : FD_LOG_CRIT(( "corrupt element returned from funk rec pool" ));
283 0 : }
284 2179044 : if( FD_UNLIKELY( !rec ) ) {
285 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_REC );
286 0 : fd_funk_rec_txn_release( txn_query );
287 0 : return rec;
288 0 : }
289 :
290 2179044 : fd_funk_val_init( rec );
291 2179044 : if( txn == NULL ) {
292 306 : fd_funk_txn_xid_set_root( rec->pair.xid );
293 2178738 : } else {
294 2178738 : fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
295 2178738 : prepare->rec_head_idx = &txn->rec_head_idx;
296 2178738 : prepare->rec_tail_idx = &txn->rec_tail_idx;
297 2178738 : }
298 2179044 : fd_funk_rec_key_copy( rec->pair.key, key );
299 2179044 : rec->tag = 0;
300 2179044 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
301 2179044 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
302 2179044 : fd_funk_rec_txn_release( txn_query );
303 2179044 : return rec;
304 2179044 : }
305 :
306 : #if FD_HAS_ATOMIC
307 :
308 : static void
309 : fd_funk_rec_push_tail( fd_funk_t * funk,
310 2178702 : fd_funk_rec_prepare_t * prepare ) {
311 2178702 : fd_funk_rec_t * rec = prepare->rec;
312 2178702 : uint rec_idx = (uint)( rec - funk->rec_pool->ele );
313 2178702 : uint * rec_head_idx = prepare->rec_head_idx;
314 2178702 : uint * rec_tail_idx = prepare->rec_tail_idx;
315 :
316 2178702 : for(;;) {
317 :
318 : /* Doubly linked list append. Robust in the event of concurrent
319 : publishes. Iteration during publish not supported. Sequence:
320 : - Identify tail element
321 : - Set new element's prev and next pointers
322 : - Set tail element's next pointer
323 : - Set tail pointer */
324 :
325 2178702 : uint rec_prev_idx = FD_VOLATILE_CONST( *rec_tail_idx );
326 2178702 : rec->prev_idx = rec_prev_idx;
327 2178702 : FD_COMPILER_MFENCE();
328 :
329 2178702 : uint * next_idx_p;
330 2178702 : if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
331 388455 : next_idx_p = rec_head_idx;
332 1790247 : } else {
333 1790247 : next_idx_p = &funk->rec_pool->ele[ rec_prev_idx ].next_idx;
334 1790247 : }
335 :
336 2178702 : fd_racesan_hook( "funk_rec_push_tail:next_cas" );
337 2178702 : if( FD_UNLIKELY( !__sync_bool_compare_and_swap( next_idx_p, FD_FUNK_REC_IDX_NULL, rec_idx ) ) ) {
338 : /* Another thread beat us to the punch */
339 0 : FD_SPIN_PAUSE();
340 0 : continue;
341 0 : }
342 :
343 2178702 : fd_racesan_hook( "funk_rec_push_tail:tail_cas" );
344 2178702 : if( FD_UNLIKELY( !__sync_bool_compare_and_swap( rec_tail_idx, rec_prev_idx, rec_idx ) ) ) {
345 : /* This CAS is guaranteed to succeed if the previous CAS passed. */
346 0 : FD_LOG_CRIT(( "Irrecoverable data race encountered while appending to txn rec list (invariant violation?): cas(%p,%u,%u)",
347 0 : (void *)rec_tail_idx, rec_prev_idx, rec_idx ));
348 0 : }
349 :
350 2178702 : break;
351 2178702 : }
352 2178702 : }
353 :
354 : #else
355 :
356 : static void
357 : fd_funk_rec_push_tail( fd_funk_t * funk,
358 : fd_funk_rec_prepare_t * prepare ) {
359 : fd_funk_rec_t * rec = prepare->rec;
360 : uint rec_idx = (uint)( rec - funk->rec_pool->ele );
361 : uint * rec_head_idx = prepare->rec_head_idx;
362 : uint * rec_tail_idx = prepare->rec_tail_idx;
363 : uint rec_prev_idx = *rec_tail_idx;
364 : *rec_tail_idx = rec_idx;
365 : rec->prev_idx = rec_prev_idx;
366 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
367 : if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
368 : *rec_head_idx = rec_idx;
369 : } else {
370 : funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
371 : }
372 : }
373 :
374 : #endif
375 :
376 : void
377 : fd_funk_rec_publish( fd_funk_t * funk,
378 2178999 : fd_funk_rec_prepare_t * prepare ) {
379 2178999 : fd_funk_rec_t * rec = prepare->rec;
380 2178999 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
381 2178999 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
382 :
383 2178999 : if( prepare->rec_head_idx ) {
384 2178702 : fd_funk_rec_push_tail( funk, prepare );
385 2178702 : }
386 :
387 2178999 : fd_racesan_hook( "funk_rec_publish:map_insert" );
388 2178999 : int insert_err = fd_funk_rec_map_insert( funk->rec_map, rec, FD_MAP_FLAG_BLOCKING );
389 2178999 : if( insert_err ) {
390 0 : FD_LOG_CRIT(( "fd_funk_rec_map_insert failed (%i-%s)", insert_err, fd_map_strerror( insert_err ) ));
391 0 : }
392 2178999 : }
393 :
394 : void
395 : fd_funk_rec_cancel( fd_funk_t * funk,
396 45 : fd_funk_rec_prepare_t * prepare ) {
397 45 : fd_funk_val_flush( prepare->rec, funk->alloc, funk->wksp );
398 45 : fd_funk_rec_pool_release( funk->rec_pool, prepare->rec, 1 );
399 45 : }
400 :
401 : fd_funk_rec_t *
402 : fd_funk_rec_clone( fd_funk_t * funk,
403 : fd_funk_txn_xid_t const * xid,
404 : fd_funk_rec_key_t const * key,
405 : fd_funk_rec_prepare_t * prepare,
406 45 : int * opt_err ) {
407 45 : fd_funk_rec_t * new_rec = fd_funk_rec_prepare( funk, xid, key, prepare, opt_err );
408 45 : if( !new_rec ) return NULL;
409 :
410 45 : for(;;) {
411 45 : fd_funk_rec_query_t query[1];
412 45 : fd_funk_rec_t const * old_rec = fd_funk_rec_query_try_global( funk, xid, key, NULL, query );
413 45 : if( !old_rec ) {
414 45 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_KEY );
415 45 : fd_funk_rec_cancel( funk, prepare );
416 45 : return NULL;
417 45 : }
418 :
419 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
420 0 : ulong val_sz = old_rec->val_sz;
421 0 : void * buf = fd_funk_val_truncate(
422 0 : new_rec,
423 0 : fd_funk_alloc( funk ),
424 0 : wksp,
425 0 : 0UL,
426 0 : val_sz,
427 0 : opt_err );
428 0 : if( !buf ) {
429 0 : fd_funk_rec_cancel( funk, prepare );
430 0 : return NULL;
431 0 : }
432 0 : memcpy( buf, fd_funk_val( old_rec, wksp ), val_sz );
433 :
434 0 : if( !fd_funk_rec_query_test( query ) ) {
435 0 : return new_rec;
436 0 : }
437 0 : }
438 45 : }
439 :
440 : int
441 12 : fd_funk_rec_verify( fd_funk_t * funk ) {
442 12 : fd_funk_rec_map_t * rec_map = funk->rec_map;
443 12 : fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
444 12 : ulong rec_max = fd_funk_rec_pool_ele_max( rec_pool );
445 :
446 24 : # define TEST(c) do { \
447 24 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
448 24 : } while(0)
449 :
450 12 : TEST( !fd_funk_rec_map_verify( rec_map ) );
451 12 : TEST( !fd_funk_rec_pool_verify( rec_pool ) );
452 :
453 : /* Iterate (again) over all records in use */
454 :
455 12 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
456 786450 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
457 786438 : for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
458 786438 : !fd_funk_rec_map_iter_done( iter );
459 786438 : iter = fd_funk_rec_map_iter_next( iter ) ) {
460 0 : fd_funk_rec_t const * rec = fd_funk_rec_map_iter_ele_const( iter );
461 :
462 : /* Make sure every record either links up with the last published
463 : transaction or an in-prep transaction and the flags are sane. */
464 :
465 0 : fd_funk_txn_xid_t const * xid = fd_funk_rec_xid( rec );
466 :
467 0 : if( fd_funk_txn_xid_eq_root( xid ) ) { /* This is a record from the last published transaction */
468 :
469 0 : TEST( fd_funk_txn_xid_eq_root( xid ) );
470 : /* No record linked list at the root txn */
471 0 : TEST( fd_funk_rec_idx_is_null( rec->prev_idx ) );
472 0 : TEST( fd_funk_rec_idx_is_null( rec->next_idx ) );
473 :
474 0 : } else { /* This is a record from an in-prep transaction */
475 :
476 0 : TEST( fd_funk_txn_query( xid, funk->txn_map ) );
477 :
478 0 : }
479 0 : }
480 786438 : }
481 :
482 : /* Clear record tags and then verify membership */
483 :
484 1572876 : for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool->ele[ rec_idx ].tag = 0;
485 :
486 12 : do {
487 12 : fd_funk_all_iter_t iter[1];
488 12 : for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
489 0 : fd_funk_rec_t * rec = fd_funk_all_iter_ele( iter );
490 0 : if( fd_funk_txn_xid_eq_root( rec->pair.xid ) ) {
491 0 : TEST( !rec->tag );
492 0 : rec->tag = 1;
493 0 : }
494 0 : }
495 :
496 12 : fd_funk_txn_all_iter_t txn_iter[1];
497 12 : 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 ) ) {
498 0 : fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
499 :
500 0 : uint rec_idx = txn->rec_head_idx;
501 0 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
502 0 : TEST( (rec_idx<rec_max) && !rec_pool->ele[ rec_idx ].tag );
503 0 : rec_pool->ele[ rec_idx ].tag = 1;
504 0 : fd_funk_rec_query_t query[1];
505 0 : fd_funk_rec_t const * rec2 = fd_funk_rec_query_try_global( funk, &txn->xid, rec_pool->ele[ rec_idx ].pair.key, NULL, query );
506 0 : TEST( rec2 == rec_pool->ele + rec_idx );
507 0 : uint next_idx = rec_pool->ele[ rec_idx ].next_idx;
508 0 : if( !fd_funk_rec_idx_is_null( next_idx ) ) TEST( rec_pool->ele[ next_idx ].prev_idx==rec_idx );
509 0 : rec_idx = next_idx;
510 0 : }
511 0 : }
512 12 : } while(0);
513 :
514 12 : do {
515 12 : fd_funk_txn_all_iter_t txn_iter[1];
516 12 : 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 ) ) {
517 0 : fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
518 :
519 0 : uint rec_idx = txn->rec_tail_idx;
520 0 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
521 0 : TEST( (rec_idx<rec_max) && rec_pool->ele[ rec_idx ].tag );
522 0 : rec_pool->ele[ rec_idx ].tag = 0;
523 0 : uint prev_idx = rec_pool->ele[ rec_idx ].prev_idx;
524 0 : if( !fd_funk_rec_idx_is_null( prev_idx ) ) TEST( rec_pool->ele[ prev_idx ].next_idx==rec_idx );
525 0 : rec_idx = prev_idx;
526 0 : }
527 0 : }
528 12 : } while(0);
529 :
530 12 : fd_funk_all_iter_t iter[1];
531 12 : for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
532 0 : fd_funk_rec_t const * rec = fd_funk_all_iter_ele( iter );
533 0 : FD_TEST( rec->tag == fd_funk_txn_xid_eq_root( rec->pair.xid ) );
534 0 : }
535 :
536 12 : # undef TEST
537 :
538 12 : return FD_FUNK_SUCCESS;
539 12 : }
|