@@ -190,14 +190,14 @@ fd_forest_verify( fd_forest_t const * forest ) {
190
190
/* query queries for a connected ele keyed by slot. does not return
191
191
orphaned ele. */
192
192
193
- static fd_forest_ele_t *
194
- ancestry_frontier_query ( fd_forest_t * forest , ulong slot ) {
195
- fd_forest_ele_t * pool = fd_forest_pool ( forest );
196
- fd_forest_ele_t * ele = NULL ;
197
- ele = fd_forest_ancestry_ele_query ( fd_forest_ancestry ( forest ), & slot , NULL , pool );
198
- ele = fd_ptr_if ( !ele , fd_forest_frontier_ele_query ( fd_forest_frontier ( forest ), & slot , NULL , pool ), ele );
199
- return ele ;
200
- }
193
+ // static fd_forest_ele_t * FD_FN_UNUSED
194
+ // ancestry_frontier_query( fd_forest_t * forest, ulong slot ) {
195
+ // fd_forest_ele_t * pool = fd_forest_pool( forest );
196
+ // fd_forest_ele_t * ele = NULL;
197
+ // ele = fd_forest_ancestry_ele_query( fd_forest_ancestry( forest ), &slot, NULL, pool );
198
+ // ele = fd_ptr_if( !ele, fd_forest_frontier_ele_query( fd_forest_frontier( forest ), &slot, NULL, pool ), ele );
199
+ // return ele;
200
+ // }
201
201
202
202
/* remove removes and returns a connected ele from ancestry or frontier
203
203
maps. does not remove orphaned ele. does not unlink ele. */
@@ -352,6 +352,9 @@ insert( fd_forest_t * forest, ulong slot, ushort parent_off ) {
352
352
# if FD_FOREST_USE_HANDHOLDING
353
353
FD_TEST ( parent_off <= slot ); /* caller err - inval */
354
354
FD_TEST ( fd_forest_pool_free ( pool ) ); /* impl err - oom */
355
+ if ( slot <= fd_forest_root_slot ( forest ) ) {
356
+ __asm__("int $3" );
357
+ }
355
358
FD_TEST ( slot > fd_forest_root_slot ( forest ) ); /* caller error - inval */
356
359
# endif
357
360
@@ -381,9 +384,6 @@ insert( fd_forest_t * forest, ulong slot, ushort parent_off ) {
381
384
382
385
fd_forest_ele_t *
383
386
fd_forest_query ( fd_forest_t * forest , ulong slot ) {
384
- # if FD_FOREST_USE_HANDHOLDING
385
- FD_TEST ( slot > fd_forest_root_slot ( forest ) ); /* caller error - inval */
386
- # endif
387
387
return query ( forest , slot );
388
388
}
389
389
@@ -432,22 +432,39 @@ fd_forest_publish( fd_forest_t * forest, ulong new_root_slot ) {
432
432
433
433
fd_forest_ancestry_t * ancestry = fd_forest_ancestry ( forest );
434
434
fd_forest_frontier_t * frontier = fd_forest_frontier ( forest );
435
+ fd_forest_orphaned_t * orphaned = fd_forest_orphaned ( forest );
435
436
fd_forest_ele_t * pool = fd_forest_pool ( forest );
436
437
ulong null = fd_forest_pool_idx_null ( pool );
437
438
438
439
fd_forest_ele_t * old_root_ele = fd_forest_pool_ele ( pool , forest -> root );
439
- fd_forest_ele_t * new_root_ele = ancestry_frontier_query ( forest , new_root_slot );
440
+ fd_forest_ele_t * new_root_ele = query ( forest , new_root_slot );
440
441
441
- # if FD_FOREST_USE_HANDHOLDING
442
- FD_TEST ( new_root_ele ); /* caller error - not found */
443
- FD_TEST ( new_root_ele -> slot > old_root_ele -> slot ); /* caller error - inval */
444
- # endif
442
+ #if FD_FOREST_USE_HANDHOLDING
443
+ if ( FD_LIKELY ( new_root_ele ) ) {
444
+ FD_TEST ( new_root_ele -> slot > old_root_ele -> slot ); /* caller error - inval */
445
+ }
446
+ #endif
447
+
448
+ /* Edge case where if we haven't been getting repairs, and we have a
449
+ gap between the root and orphans. we publish forward to a slot that
450
+ we don't have. This only case this should be happening is when we
451
+ load a second incremental and that incremental slot lives in the
452
+ gap. In that case this isn't a bug, but we should be treating this
453
+ new root like the snapshot slot / init root. Should be happening
454
+ very rarely given a well-functioning repair. */
455
+
456
+ if ( FD_UNLIKELY ( !new_root_ele ) ) {
457
+ new_root_ele = acquire ( forest , new_root_slot );
458
+ new_root_ele -> complete_idx = 0 ;
459
+ new_root_ele -> buffered_idx = 0 ;
460
+ fd_forest_frontier_ele_insert ( frontier , new_root_ele , pool );
461
+ }
445
462
446
463
/* First, remove the previous root, and add it to a FIFO prune queue.
447
464
head points to the queue head (initialized with old_root_ele). */
448
465
449
466
fd_forest_ele_t * head = ancestry_frontier_remove ( forest , old_root_ele -> slot );
450
- head -> next = null ;
467
+ head -> prev = null ;
451
468
fd_forest_ele_t * tail = head ;
452
469
453
470
/* Second, BFS down the tree, inserting each ele into the prune queue
@@ -460,22 +477,85 @@ fd_forest_publish( fd_forest_t * forest, ulong new_root_slot ) {
460
477
if ( FD_LIKELY ( child != new_root_ele ) ) { /* do not prune new root or descendants */
461
478
ulong idx = fd_forest_ancestry_idx_remove ( ancestry , & child -> slot , null , pool );
462
479
idx = fd_ulong_if ( idx != null , idx , fd_forest_frontier_idx_remove ( frontier , & child -> slot , null , pool ) );
463
- tail -> next = idx ; /* insert prune queue */
480
+ tail -> prev = idx ; /* insert prune queue */
464
481
# if FD_FOREST_USE_HANDHOLDING
465
- FD_TEST ( tail -> next != null ); /* programming error in BFS */
482
+ FD_TEST ( tail -> prev != null ); /* programming error in BFS */
466
483
# endif
467
- tail = fd_forest_pool_ele ( pool , tail -> next ); /* advance prune queue */
468
- tail -> next = null ;
484
+ tail = fd_forest_pool_ele ( pool , tail -> prev ); /* advance prune queue */
485
+ tail -> prev = null ;
469
486
}
470
487
child = fd_forest_pool_ele ( pool , child -> sibling );
471
488
}
472
- fd_forest_ele_t * next = fd_forest_pool_ele ( pool , head -> next ); /* FIFO pop */
489
+ fd_forest_ele_t * next = fd_forest_pool_ele ( pool , head -> prev ); /* FIFO pop */
473
490
fd_forest_pool_ele_release ( pool , head ); /* free head */
474
491
head = next ;
475
492
}
476
493
494
+ /* If there is nothing on the frontier, we have hit an edge case
495
+ during catching up where all of our frontiers were < the new root.
496
+ In that case we need to continue repairing from the new root, so
497
+ add it to the frontier. */
498
+
499
+ if ( FD_UNLIKELY ( fd_forest_frontier_iter_done ( fd_forest_frontier_iter_init ( frontier , pool ), frontier , pool ) ) ) {
500
+ fd_forest_ele_t * remove = fd_forest_ancestry_ele_remove ( ancestry , & new_root_ele -> slot , NULL , pool );
501
+ if ( FD_UNLIKELY ( !remove ) ) {
502
+ /* Very rare case where during boot we could publish to an orphaned slot */
503
+ remove = fd_forest_orphaned_ele_remove ( orphaned , & new_root_ele -> slot , NULL , pool );
504
+ }
505
+ FD_TEST ( remove == new_root_ele );
506
+ fd_forest_frontier_ele_insert ( frontier , new_root_ele , pool );
507
+ new_root_ele -> complete_idx = 0 ;
508
+ new_root_ele -> buffered_idx = 0 ;
509
+ advance_frontier ( forest , new_root_ele -> slot , 0 );
510
+ }
511
+
477
512
new_root_ele -> parent = null ; /* unlink new root from parent */
478
- forest -> root = fd_forest_ancestry_idx_query ( ancestry , & new_root_slot , null , pool );
513
+ forest -> root = fd_forest_pool_idx ( pool , new_root_ele );
514
+
515
+ FD_TEST ( !fd_forest_verify ( forest ) );
516
+
517
+ /* Lastly, cleanup orphans if there orphan heads < new_root_slot.
518
+ First, add any relevant orphans to the prune queue. FIXME: NEED TO REMOVE BEFORE MODIFYING NEXT PTR */
519
+
520
+ head = NULL ;
521
+ for ( fd_forest_orphaned_iter_t iter = fd_forest_orphaned_iter_init ( orphaned , pool );
522
+ !fd_forest_orphaned_iter_done ( iter , orphaned , pool );
523
+ iter = fd_forest_orphaned_iter_next ( iter , orphaned , pool ) ) {
524
+ fd_forest_ele_t * ele = fd_forest_orphaned_iter_ele ( iter , orphaned , pool );
525
+ if ( FD_UNLIKELY ( ele -> slot < new_root_slot ) ) {
526
+ if ( FD_UNLIKELY ( !head ) ) {
527
+ head = ele ;
528
+ head -> prev = null ;
529
+ tail = ele ;
530
+ } else {
531
+ tail -> prev = iter .ele_idx ;
532
+ tail = fd_forest_pool_ele ( pool , tail -> prev );
533
+ tail -> prev = null ;
534
+ }
535
+ }
536
+ }
537
+
538
+ FD_TEST ( !fd_forest_verify ( forest ) );
539
+
540
+
541
+ /* Now BFS and clean up children of these orphan heads */
542
+ while ( head ) {
543
+ fd_forest_ele_t * child = fd_forest_pool_ele ( pool , head -> child );
544
+ while ( FD_LIKELY ( child ) ) {
545
+ if ( FD_LIKELY ( child != new_root_ele ) ) {
546
+ tail -> prev = fd_forest_pool_idx ( pool , child ); /* insert prune queue */
547
+ tail = fd_forest_pool_ele ( pool , tail -> prev ); /* advance prune queue */
548
+ tail -> prev = null ;
549
+ }
550
+ child = fd_forest_pool_ele ( pool , child -> sibling );
551
+ }
552
+ ulong remove = fd_forest_orphaned_idx_remove ( orphaned , & head -> slot , null , pool ); /* remove myself */
553
+ remove = fd_ulong_if ( remove == null , fd_forest_ancestry_idx_remove ( ancestry , & head -> slot , null , pool ), remove );
554
+
555
+ fd_forest_ele_t * next = fd_forest_pool_ele ( pool , head -> prev ); /* FIFO pop */
556
+ fd_forest_pool_ele_release ( pool , head ); /* free head */
557
+ head = next ;
558
+ }
479
559
return new_root_ele ;
480
560
}
481
561
0 commit comments