Line data Source code
1 : #include "fd_forks.h"
2 :
3 : #include "../../flamenco/runtime/context/fd_exec_slot_ctx.h"
4 : #include "../../flamenco/runtime/fd_acc_mgr.h"
5 : #include "../../flamenco/runtime/fd_borrowed_account.h"
6 : #include "../../flamenco/runtime/fd_runtime.h"
7 : #include "../../flamenco/runtime/program/fd_program_util.h"
8 : #include "../../flamenco/runtime/program/fd_vote_program.h"
9 :
10 : void *
11 0 : fd_forks_new( void * shmem, ulong max, ulong seed ) {
12 :
13 0 : if( FD_UNLIKELY( !shmem ) ) {
14 0 : FD_LOG_WARNING( ( "NULL mem" ) );
15 0 : return NULL;
16 0 : }
17 :
18 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_forks_align() ) ) ) {
19 0 : FD_LOG_WARNING( ( "misaligned mem" ) );
20 0 : return NULL;
21 0 : }
22 :
23 0 : ulong footprint = fd_forks_footprint( max );
24 0 : if( FD_UNLIKELY( !footprint ) ) {
25 0 : FD_LOG_WARNING( ( "bad mem" ) );
26 0 : return NULL;
27 0 : }
28 :
29 0 : fd_memset( shmem, 0, footprint );
30 0 : ulong laddr = (ulong)shmem;
31 :
32 0 : laddr = fd_ulong_align_up( laddr, alignof( fd_forks_t ) );
33 0 : laddr += sizeof( fd_forks_t );
34 :
35 0 : laddr = fd_ulong_align_up( laddr, fd_fork_pool_align() );
36 0 : fd_fork_pool_new( (void *)laddr, max );
37 0 : laddr += fd_fork_pool_footprint( max );
38 :
39 0 : laddr = fd_ulong_align_up( laddr, fd_fork_frontier_align() );
40 0 : fd_fork_frontier_new( (void *)laddr, max, seed );
41 0 : laddr += fd_fork_frontier_footprint( max );
42 :
43 0 : return shmem;
44 0 : }
45 :
46 : fd_forks_t *
47 0 : fd_forks_join( void * shforks ) {
48 :
49 0 : if( FD_UNLIKELY( !shforks ) ) {
50 0 : FD_LOG_WARNING( ( "NULL forks" ) );
51 0 : return NULL;
52 0 : }
53 :
54 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shforks, fd_forks_align() ) ) ) {
55 0 : FD_LOG_WARNING( ( "misaligned forks" ) );
56 0 : return NULL;
57 0 : }
58 :
59 0 : ulong laddr = (ulong)shforks;
60 0 : fd_forks_t * forks = (void *)laddr;
61 :
62 0 : laddr = fd_ulong_align_up( laddr, alignof( fd_forks_t ) );
63 0 : laddr += sizeof( fd_forks_t );
64 :
65 0 : laddr = fd_ulong_align_up( laddr, fd_fork_pool_align() );
66 0 : forks->pool = fd_fork_pool_join( (void *)laddr );
67 0 : ulong max = fd_fork_pool_max( forks->pool );
68 0 : laddr += fd_fork_pool_footprint( max );
69 :
70 0 : laddr = fd_ulong_align_up( laddr, fd_fork_frontier_align() );
71 0 : forks->frontier = fd_fork_frontier_join( (void *)laddr );
72 0 : laddr += fd_fork_frontier_footprint( max );
73 :
74 0 : return (fd_forks_t *)shforks;
75 0 : }
76 :
77 : void *
78 0 : fd_forks_leave( fd_forks_t const * forks ) {
79 :
80 0 : if( FD_UNLIKELY( !forks ) ) {
81 0 : FD_LOG_WARNING( ( "NULL forks" ) );
82 0 : return NULL;
83 0 : }
84 :
85 0 : return (void *)forks;
86 0 : }
87 :
88 : void *
89 0 : fd_forks_delete( void * forks ) {
90 :
91 0 : if( FD_UNLIKELY( !forks ) ) {
92 0 : FD_LOG_WARNING( ( "NULL forks" ) );
93 0 : return NULL;
94 0 : }
95 :
96 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)forks, fd_forks_align() ) ) ) {
97 0 : FD_LOG_WARNING( ( "misaligned forks" ) );
98 0 : return NULL;
99 0 : }
100 :
101 0 : return forks;
102 0 : }
103 :
104 : fd_fork_t *
105 0 : fd_forks_init( fd_forks_t * forks, fd_exec_slot_ctx_t const * slot_ctx ) {
106 :
107 0 : if( FD_UNLIKELY( !forks ) ) {
108 0 : FD_LOG_WARNING( ( "NULL forks" ) );
109 0 : return NULL;
110 0 : }
111 :
112 0 : if( FD_UNLIKELY( !slot_ctx ) ) {
113 0 : FD_LOG_WARNING( ( "NULL slot_ctx" ) );
114 0 : return NULL;
115 0 : }
116 :
117 0 : fd_fork_t * fork = fd_fork_pool_ele_acquire( forks->pool );
118 0 : fork->slot = slot_ctx->slot_bank.slot;
119 0 : fork->prev = fd_fork_pool_idx_null( forks->pool );
120 0 : fork->lock = 0;
121 0 : fork->slot_ctx = *slot_ctx; /* this shallow copy is only safe if
122 : the lifetimes of slot_ctx's pointers
123 : are as long as fork */
124 0 : if( FD_UNLIKELY( !fd_fork_frontier_ele_insert( forks->frontier, fork, forks->pool ) ) ) {
125 0 : FD_LOG_WARNING( ( "Failed to insert fork into frontier" ) );
126 0 : }
127 :
128 0 : return fork;
129 0 : }
130 :
131 : fd_fork_t *
132 0 : fd_forks_query( fd_forks_t * forks, ulong slot ) {
133 0 : return fd_fork_frontier_ele_query( forks->frontier, &slot, NULL, forks->pool );
134 0 : }
135 :
136 : fd_fork_t const *
137 0 : fd_forks_query_const( fd_forks_t const * forks, ulong slot ) {
138 0 : return fd_fork_frontier_ele_query_const( forks->frontier, &slot, NULL, forks->pool );
139 0 : }
140 :
141 : // fd_fork_t *
142 : // fd_forks_advance( fd_forks_t * forks,
143 : // fd_fork_t * fork,
144 : // ulong slot,
145 : // fd_acc_mgr_t * acc_mgr,
146 : // fd_blockstore_t * blockstore,
147 : // fd_exec_epoch_ctx_t * epoch_ctx,
148 : // fd_funk_t * funk,
149 : // fd_valloc_t valloc ) {
150 : // // Remove slot ctx from frontier
151 : // fd_fork_t * child = fd_fork_frontier_ele_remove( forks->frontier,
152 : // &fork->slot,
153 : // NULL,
154 : // forks->pool );
155 : // child->slot = curr_slot;
156 : // if( FD_UNLIKELY( fd_fork_frontier_ele_query( forks->frontier,
157 : // &curr_slot,
158 : // NULL,
159 : // forks->pool ) ) ) {
160 : // FD_LOG_ERR( ( "invariant violation: child slot %lu was already in the
161 : // frontier", curr_slot ) );
162 : // }
163 : // fd_fork_frontier_ele_insert( forks->frontier, child, forks->pool );
164 : // FD_TEST( fork == child );
165 :
166 : // // fork is advancing
167 : // FD_LOG_DEBUG(( "new block execution - slot: %lu, parent_slot: %lu", curr_slot, parent_slot ));
168 :
169 : // fork->slot_ctx.slot_bank.prev_slot = fork->slot_ctx.slot_bank.slot;
170 : // fork->slot_ctx.slot_bank.slot = curr_slot;
171 :
172 : // fork->slot_ctx.status_cache = status_cache;
173 : // fd_funk_txn_xid_t xid;
174 :
175 : // fd_memcpy( xid.uc, blockhash.uc, sizeof( fd_funk_txn_xid_t));
176 : // xid.ul[0] = fork->slot_ctx.slot_bank.slot;
177 : // /* push a new transaction on the stack */
178 : // fd_funk_start_write( funk );
179 : // fork->slot_ctx.funk_txn = fd_funk_txn_prepare( funk, fork->slot_ctx.funk_txn, &xid, 1 );
180 : // fd_funk_end_write( funk );
181 :
182 : // int res = fd_runtime_publish_old_txns( &fork->slot_ctx, capture_ctx );
183 : // if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
184 : // FD_LOG_ERR(( "txn publishing failed" ));
185 : // }
186 : // }
187 :
188 : static void
189 : slot_ctx_restore( ulong slot,
190 : fd_acc_mgr_t * acc_mgr,
191 : fd_blockstore_t * blockstore,
192 : fd_exec_epoch_ctx_t * epoch_ctx,
193 : fd_funk_t * funk,
194 : fd_valloc_t valloc,
195 0 : fd_exec_slot_ctx_t * slot_ctx_out ) {
196 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( funk, fd_funk_wksp( funk ) );
197 0 : fd_block_map_t * block = fd_block_map_query( fd_blockstore_block_map( blockstore ), &slot, NULL );
198 0 : FD_LOG_DEBUG( ( "Current slot %lu", slot ) );
199 0 : if( !block || !block->block_gaddr )
200 0 : FD_LOG_ERR( ( "missing block at slot we're trying to restore" ) );
201 0 : fd_funk_txn_xid_t xid;
202 0 : memcpy( xid.uc, block->block_hash.uc, sizeof( fd_funk_txn_xid_t ) );
203 0 : xid.ul[0] = slot;
204 0 : fd_funk_rec_key_t id = fd_runtime_slot_bank_key();
205 0 : fd_funk_txn_t * txn = fd_funk_txn_query( &xid, txn_map );
206 0 : if( !txn ) {
207 0 : memset( xid.uc, 0, sizeof( fd_funk_txn_xid_t ) );
208 0 : xid.ul[0] = slot;
209 0 : txn = fd_funk_txn_query( &xid, txn_map );
210 0 : if( !txn ) {
211 0 : FD_LOG_ERR( ( "missing txn, parent slot %lu", slot ) );
212 0 : }
213 0 : }
214 0 : fd_funk_rec_t const * rec = fd_funk_rec_query_global( funk, txn, &id, NULL );
215 0 : if( rec == NULL ) FD_LOG_ERR( ( "failed to read banks record" ) );
216 0 : void * val = fd_funk_val( rec, fd_funk_wksp( funk ) );
217 :
218 0 : uint magic = *(uint *)val;
219 :
220 0 : fd_bincode_decode_ctx_t decode_ctx;
221 0 : decode_ctx.data = (uchar *)val + sizeof( uint );
222 0 : decode_ctx.dataend = (uchar *)val + fd_funk_val_sz( rec );
223 0 : decode_ctx.valloc = valloc;
224 :
225 0 : FD_TEST( slot_ctx_out->magic == FD_EXEC_SLOT_CTX_MAGIC );
226 :
227 0 : slot_ctx_out->funk_txn = txn;
228 :
229 0 : slot_ctx_out->acc_mgr = acc_mgr;
230 0 : slot_ctx_out->blockstore = blockstore;
231 0 : slot_ctx_out->epoch_ctx = epoch_ctx;
232 0 : slot_ctx_out->valloc = valloc;
233 :
234 0 : fd_bincode_destroy_ctx_t destroy_ctx = {
235 0 : .valloc = valloc,
236 0 : };
237 :
238 0 : fd_slot_bank_destroy( &slot_ctx_out->slot_bank, &destroy_ctx );
239 0 : if( magic == FD_RUNTIME_ENC_BINCODE ) {
240 0 : FD_TEST( fd_slot_bank_decode( &slot_ctx_out->slot_bank, &decode_ctx ) == FD_BINCODE_SUCCESS );
241 0 : } else if( magic == FD_RUNTIME_ENC_ARCHIVE ) {
242 0 : FD_TEST( fd_slot_bank_decode_archival( &slot_ctx_out->slot_bank, &decode_ctx ) ==
243 0 : FD_BINCODE_SUCCESS );
244 0 : } else {
245 0 : FD_LOG_ERR( ( "failed to read banks record: invalid magic number" ) );
246 0 : }
247 0 : FD_TEST( !fd_runtime_sysvar_cache_load( slot_ctx_out ) );
248 :
249 : // TODO how do i get this info, ignoring rewards for now
250 : // slot_ctx_out->epoch_reward_status = ???
251 :
252 : // signature_cnt, account_delta_hash, prev_banks_hash are used for the banks
253 : // hash calculation and not needed when restoring parent
254 :
255 0 : FD_LOG_NOTICE( ( "recovered slot_bank for slot=%lu banks_hash=%s poh_hash %s",
256 0 : slot_ctx_out->slot_bank.slot,
257 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx_out->slot_bank.banks_hash.hash ),
258 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx_out->slot_bank.poh.hash ) ) );
259 :
260 : /* Prepare bank for next slot */
261 0 : slot_ctx_out->slot_bank.slot = slot;
262 0 : slot_ctx_out->slot_bank.collected_execution_fees = 0;
263 0 : slot_ctx_out->slot_bank.collected_priority_fees = 0;
264 0 : slot_ctx_out->slot_bank.collected_rent = 0;
265 :
266 : /* FIXME epoch boundary stuff when replaying */
267 : // fd_features_restore( slot_ctx );
268 : // fd_runtime_update_leaders( slot_ctx, slot_ctx->slot_bank.slot );
269 : // fd_calculate_epoch_accounts_hash_values( slot_ctx );
270 0 : }
271 :
272 : fd_fork_t *
273 : fd_forks_prepare( fd_forks_t const * forks,
274 : ulong parent_slot,
275 : fd_acc_mgr_t * acc_mgr,
276 : fd_blockstore_t * blockstore,
277 : fd_exec_epoch_ctx_t * epoch_ctx,
278 : fd_funk_t * funk,
279 0 : fd_valloc_t valloc ) {
280 :
281 : /* Check the parent block is present in the blockstore and executed. */
282 :
283 0 : fd_blockstore_start_read( blockstore );
284 0 : fd_block_t * block = fd_blockstore_block_query( blockstore, parent_slot );
285 0 : fd_blockstore_end_read( blockstore );
286 0 : if( FD_UNLIKELY( !block ) ) {
287 0 : FD_LOG_WARNING( ( "fd_forks_prepare missing parent_slot %lu", parent_slot ) );
288 0 : }
289 :
290 : /* Query for parent_slot in the frontier. */
291 :
292 0 : fd_fork_t * fork = fd_fork_frontier_ele_query( forks->frontier, &parent_slot, NULL, forks->pool );
293 :
294 : /* If the parent block is both present and executed, but isn't in the
295 : frontier, that means this block is starting a new fork and needs to
296 : be added to the frontier. This requires recovering the slot_ctx
297 : as of that parent_slot by executing a funky rollback. */
298 :
299 0 : if( FD_UNLIKELY( !fork ) ) {
300 :
301 : /* Alloc a new slot_ctx */
302 :
303 0 : fork = fd_fork_pool_ele_acquire( forks->pool );
304 0 : fork->prev = fd_fork_pool_idx_null( forks->pool );
305 0 : fork->slot = parent_slot;
306 0 : fork->lock = 1;
307 :
308 : /* Format and join the slot_ctx */
309 :
310 0 : fd_exec_slot_ctx_t * slot_ctx = fd_exec_slot_ctx_join( fd_exec_slot_ctx_new( &fork->slot_ctx,
311 0 : valloc ) );
312 0 : if( FD_UNLIKELY( !slot_ctx ) ) {
313 0 : FD_LOG_ERR( ( "failed to new and join slot_ctx" ) );
314 0 : }
315 :
316 : /* Restore and decode w/ funk */
317 :
318 0 : slot_ctx_restore( fork->slot, acc_mgr, blockstore, epoch_ctx, funk, valloc, slot_ctx );
319 :
320 : /* Add to frontier */
321 :
322 0 : fd_fork_frontier_ele_insert( forks->frontier, fork, forks->pool );
323 0 : }
324 :
325 0 : return fork;
326 0 : }
327 :
328 : void
329 : fd_forks_update( fd_forks_t * forks,
330 : fd_blockstore_t * blockstore,
331 : fd_epoch_t * epoch,
332 : fd_funk_t * funk,
333 : fd_ghost_t * ghost,
334 0 : ulong slot ) {
335 0 : fd_fork_t * fork = fd_fork_frontier_ele_query( forks->frontier, &slot, NULL, forks->pool );
336 0 : fd_funk_txn_t * txn = fork->slot_ctx.funk_txn;
337 :
338 0 : fd_voter_t * epoch_voters = fd_epoch_voters( epoch );
339 0 : for( ulong i = 0; i < fd_epoch_voters_slot_cnt( epoch_voters ); i++ ) {
340 0 : if( FD_LIKELY( fd_epoch_voters_key_inval( epoch_voters[i].key ) ) ) continue /* most slots are empty */;
341 :
342 : /* TODO we can optimize this funk query to only check through the
343 : last slot on this fork this function was called on. currently
344 : rec_query_global traverses all the way back to the root. */
345 :
346 0 : fd_voter_t * voter = &epoch_voters[i];
347 0 : fd_voter_state_t const * state = fd_voter_state( funk, txn, &voter->rec );
348 0 : ulong vote = fd_voter_state_vote( state );
349 :
350 : /* Only process votes for slots >= root. Ghost requires vote slot
351 : to already exist in the ghost tree. */
352 :
353 0 : if( FD_UNLIKELY( vote != FD_SLOT_NULL && vote >= fd_ghost_root( ghost )->slot ) ) {
354 0 : fd_ghost_replay_vote( ghost, voter, vote );
355 :
356 : /* Check if it has crossed the equivocation safety and optimistic confirmation thresholds. */
357 :
358 0 : fd_ghost_node_t const * node = fd_ghost_query( ghost, vote );
359 :
360 0 : fd_blockstore_start_write( blockstore );
361 0 : fd_block_map_t * block_map_entry = fd_blockstore_block_map_query( blockstore, vote );
362 :
363 0 : int eqvocsafe = fd_uchar_extract_bit( block_map_entry->flags, FD_BLOCK_FLAG_EQVOCSAFE );
364 0 : if( FD_UNLIKELY( !eqvocsafe ) ) {
365 0 : double pct = (double)node->replay_stake / (double)epoch->total_stake;
366 0 : if( FD_UNLIKELY( pct > FD_EQVOCSAFE_PCT ) ) {
367 0 : FD_LOG_DEBUG( ( "eqvocsafe %lu", block_map_entry->slot ) );
368 0 : block_map_entry->flags = fd_uchar_set_bit( block_map_entry->flags,
369 0 : FD_BLOCK_FLAG_EQVOCSAFE );
370 0 : blockstore->hcs = fd_ulong_max( blockstore->hcs, block_map_entry->slot );
371 0 : }
372 0 : }
373 :
374 0 : int confirmed = fd_uchar_extract_bit( block_map_entry->flags, FD_BLOCK_FLAG_CONFIRMED );
375 0 : if( FD_UNLIKELY( !confirmed ) ) {
376 0 : double pct = (double)node->replay_stake / (double)epoch->total_stake;
377 0 : if( FD_UNLIKELY( pct > FD_CONFIRMED_PCT ) ) {
378 0 : FD_LOG_DEBUG( ( "confirmed %lu", block_map_entry->slot ) );
379 0 : block_map_entry->flags = fd_uchar_set_bit( block_map_entry->flags,
380 0 : FD_BLOCK_FLAG_CONFIRMED );
381 0 : blockstore->hcs = fd_ulong_max( blockstore->hcs, block_map_entry->slot );
382 0 : }
383 0 : }
384 :
385 0 : fd_blockstore_end_write( blockstore );
386 0 : }
387 :
388 0 : ulong root = fd_voter_state_root( state );
389 :
390 : /* Check if this voter's root >= ghost root. We can't process
391 : other voters' roots that precede the ghost root. */
392 :
393 0 : if( FD_UNLIKELY( root >= fd_ghost_root( ghost )->slot ) ) {
394 0 : fd_ghost_node_t const * node = fd_ghost_query( ghost, root );
395 0 : if( FD_UNLIKELY( !node ) ) {
396 :
397 : /* Error if the node's root is not in ghost. This is an
398 : invariant violation because a node cannot have possibly
399 : rooted something that on the current fork that isn't in
400 : ghost. */
401 :
402 0 : FD_LOG_ERR(( "[%s] node %s's root %lu was not in ghost", __func__, FD_BASE58_ENC_32_ALLOCA(&voter->key), root ));
403 0 : }
404 :
405 0 : fd_ghost_rooted_vote( ghost, voter, root );
406 :
407 : /* Check if it has crossed finalized threshold. */
408 :
409 0 : fd_blockstore_start_write( blockstore );
410 0 : fd_block_map_t * block_map_entry = fd_blockstore_block_map_query( blockstore, root );
411 0 : int finalized = fd_uchar_extract_bit( block_map_entry->flags, FD_BLOCK_FLAG_FINALIZED );
412 0 : if( FD_UNLIKELY( !finalized ) ) {
413 0 : double pct = (double)node->rooted_stake / (double)epoch->total_stake;
414 0 : if( FD_UNLIKELY( pct > FD_FINALIZED_PCT ) ) {
415 0 : ulong smr = block_map_entry->slot;
416 0 : blockstore->smr = fd_ulong_max( blockstore->smr, smr );
417 0 : FD_LOG_DEBUG( ( "finalized %lu", block_map_entry->slot ) );
418 0 : fd_block_map_t * ancestor = block_map_entry;
419 0 : while( ancestor ) {
420 0 : ancestor->flags = fd_uchar_set_bit( ancestor->flags, FD_BLOCK_FLAG_FINALIZED );
421 0 : ancestor = fd_blockstore_block_map_query( blockstore, ancestor->parent_slot );
422 0 : }
423 0 : }
424 0 : }
425 0 : fd_blockstore_end_write( blockstore );
426 0 : }
427 0 : }
428 0 : }
429 :
430 : void
431 0 : fd_forks_publish( fd_forks_t * forks, ulong slot, fd_ghost_t const * ghost ) {
432 0 : fd_fork_t * tail = NULL;
433 0 : fd_fork_t * curr = NULL;
434 :
435 0 : for( fd_fork_frontier_iter_t iter = fd_fork_frontier_iter_init( forks->frontier, forks->pool );
436 0 : !fd_fork_frontier_iter_done( iter, forks->frontier, forks->pool );
437 0 : iter = fd_fork_frontier_iter_next( iter, forks->frontier, forks->pool ) ) {
438 0 : fd_fork_t * fork = fd_fork_frontier_iter_ele( iter, forks->frontier, forks->pool );
439 :
440 : /* Prune any forks not in the ancestry from root.
441 :
442 : Optimize for unlikely because there is usually just one fork. */
443 :
444 0 : int stale = fork->slot < slot || !fd_ghost_is_ancestor( ghost, slot, fork->slot );
445 0 : if( FD_UNLIKELY( !fork->lock && stale ) ) {
446 0 : FD_LOG_NOTICE( ( "adding %lu to prune. root %lu", fork->slot, slot ) );
447 0 : if( FD_LIKELY( !curr ) ) {
448 0 : tail = fork;
449 0 : curr = fork;
450 0 : } else {
451 0 : curr->prev = fd_fork_pool_idx( forks->pool, fork );
452 0 : curr = fd_fork_pool_ele( forks->pool, curr->prev );
453 0 : }
454 0 : }
455 0 : }
456 :
457 0 : while( FD_UNLIKELY( tail ) ) {
458 0 : fd_fork_t * fork = fd_fork_frontier_ele_query( forks->frontier,
459 0 : &tail->slot,
460 0 : NULL,
461 0 : forks->pool );
462 0 : if( FD_UNLIKELY( !fd_exec_slot_ctx_delete( fd_exec_slot_ctx_leave( &fork->slot_ctx ) ) ) ) {
463 0 : FD_LOG_ERR( ( "could not delete fork slot ctx" ) );
464 0 : }
465 0 : ulong remove = fd_fork_frontier_idx_remove( forks->frontier,
466 0 : &tail->slot,
467 0 : fd_fork_pool_idx_null( forks->pool ),
468 0 : forks->pool );
469 0 : #if FD_FORKS_USE_HANDHOLDING
470 0 : if( FD_UNLIKELY( remove == fd_fork_pool_idx_null( forks->pool ) ) ) {
471 0 : FD_LOG_ERR( ( "failed to remove fork we added to prune." ) );
472 0 : }
473 0 : #endif
474 :
475 : /* pool_idx_release cannot fail given we just removed this from the
476 : frontier directly above. */
477 0 : fd_fork_pool_idx_release( forks->pool, remove );
478 0 : tail = fd_ptr_if( tail->prev != fd_fork_pool_idx_null( forks->pool ),
479 0 : fd_fork_pool_ele( forks->pool, tail->prev ),
480 0 : NULL );
481 0 : }
482 0 : }
483 :
484 : #include <stdio.h>
485 :
486 : void
487 0 : fd_forks_print( fd_forks_t const * forks ) {
488 0 : FD_LOG_NOTICE( ( "\n\n[Forks]" ) );
489 0 : for( fd_fork_frontier_iter_t iter = fd_fork_frontier_iter_init( forks->frontier, forks->pool );
490 0 : !fd_fork_frontier_iter_done( iter, forks->frontier, forks->pool );
491 0 : iter = fd_fork_frontier_iter_next( iter, forks->frontier, forks->pool ) ) {
492 0 : printf( "%lu\n", fd_fork_frontier_iter_ele_const( iter, forks->frontier, forks->pool )->slot );
493 0 : }
494 0 : printf( "\n" );
495 0 : }
|