@@ -95,6 +95,7 @@ export const DefaultLanes: Lanes = /* */ 0b0000000000000000000
95
95
96
96
const TransitionHydrationLane : Lane = /* */ 0b0000000000000000001000000000000 ;
97
97
const TransitionLanes : Lanes = /* */ 0b0000000001111111110000000000000 ;
98
+ const SomeTransitionLane : Lane = /* */ 0b0000000000000000010000000000000 ;
98
99
99
100
const RetryLanes : Lanes = /* */ 0b0000011110000000000000000000000 ;
100
101
@@ -113,6 +114,9 @@ export const NoTimestamp = -1;
113
114
114
115
let currentUpdateLanePriority : LanePriority = NoLanePriority ;
115
116
117
+ let nextTransitionLane : Lane = SomeTransitionLane ;
118
+ let nextRetryLane : Lane = SomeRetryLane ;
119
+
116
120
export function getCurrentUpdateLanePriority ( ) : LanePriority {
117
121
return currentUpdateLanePriority ;
118
122
}
@@ -313,9 +317,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
313
317
// We don't need to do anything extra here, because we apply per-lane
314
318
// transition entanglement in the entanglement loop below.
315
319
} else {
316
- // If there are higher priority lanes, we'll include them even if they
317
- // are suspended.
318
- nextLanes = pendingLanes & getEqualOrHigherPriorityLanes ( nextLanes ) ;
320
+ // When per-lane entanglement is not enabled, always entangle all pending
321
+ // transitions together.
322
+ if ( nextLanes & TransitionLanes ) {
323
+ nextLanes |= pendingLanes & TransitionLanes ;
324
+ }
319
325
}
320
326
321
327
// If we're already in the middle of a render, switching lanes will interrupt
@@ -350,6 +356,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
350
356
// entanglement is usually "best effort": we'll try our best to render the
351
357
// lanes in the same batch, but it's not worth throwing out partially
352
358
// completed work in order to do it.
359
+ // TODO: Reconsider this. The counter-argument is that the partial work
360
+ // represents an intermediate state, which we don't want to show to the user.
361
+ // And by spending extra time finishing it, we're increasing the amount of
362
+ // time it takes to show the final state, which is what they are actually
363
+ // waiting for.
353
364
//
354
365
// For those exceptions where entanglement is semantically important, like
355
366
// useMutableSource, we should ensure that there is no partial work at the
@@ -559,34 +570,23 @@ export function findUpdateLane(
559
570
) ;
560
571
}
561
572
562
- // To ensure consistency across multiple updates in the same event, this should
563
- // be pure function, so that it always returns the same lane for given inputs.
564
- export function findTransitionLane ( wipLanes : Lanes , pendingLanes : Lanes ) : Lane {
565
- // First look for lanes that are completely unclaimed, i.e. have no
566
- // pending work.
567
- let lane = pickArbitraryLane ( TransitionLanes & ~ pendingLanes ) ;
568
- if ( lane === NoLane ) {
569
- // If all lanes have pending work, look for a lane that isn't currently
570
- // being worked on.
571
- lane = pickArbitraryLane ( TransitionLanes & ~ wipLanes ) ;
572
- if ( lane === NoLane ) {
573
- // If everything is being worked on, pick any lane. This has the
574
- // effect of interrupting the current work-in-progress.
575
- lane = pickArbitraryLane ( TransitionLanes ) ;
576
- }
573
+ export function claimNextTransitionLane ( ) : Lane {
574
+ // Cycle through the lanes, assigning each new transition to the next lane.
575
+ // In most cases, this means every transition gets its own lane, until we
576
+ // run out of lanes and cycle back to the beginning.
577
+ const lane = nextTransitionLane ;
578
+ nextTransitionLane <<= 1 ;
579
+ if ( ( nextTransitionLane & TransitionLanes ) === 0 ) {
580
+ nextTransitionLane = SomeTransitionLane ;
577
581
}
578
582
return lane ;
579
583
}
580
584
581
- // To ensure consistency across multiple updates in the same event, this should
582
- // be pure function, so that it always returns the same lane for given inputs.
583
- export function findRetryLane ( wipLanes : Lanes ) : Lane {
584
- // This is a fork of `findUpdateLane` designed specifically for Suspense
585
- // "retries" — a special update that attempts to flip a Suspense boundary
586
- // from its placeholder state to its primary/resolved state.
587
- let lane = pickArbitraryLane ( RetryLanes & ~ wipLanes ) ;
588
- if ( lane === NoLane ) {
589
- lane = pickArbitraryLane ( RetryLanes ) ;
585
+ export function claimNextRetryLane ( ) : Lane {
586
+ const lane = nextRetryLane ;
587
+ nextRetryLane <<= 1 ;
588
+ if ( ( nextRetryLane & RetryLanes ) === 0 ) {
589
+ nextRetryLane = SomeRetryLane ;
590
590
}
591
591
return lane ;
592
592
}
@@ -595,16 +595,6 @@ function getHighestPriorityLane(lanes: Lanes) {
595
595
return lanes & - lanes ;
596
596
}
597
597
598
- function getLowestPriorityLane ( lanes : Lanes ) : Lane {
599
- // This finds the most significant non-zero bit.
600
- const index = 31 - clz32 ( lanes ) ;
601
- return index < 0 ? NoLanes : 1 << index ;
602
- }
603
-
604
- function getEqualOrHigherPriorityLanes ( lanes : Lanes | Lane ) : Lanes {
605
- return ( getLowestPriorityLane ( lanes ) << 1 ) - 1 ;
606
- }
607
-
608
598
export function pickArbitraryLane ( lanes : Lanes ) : Lane {
609
599
// This wrapper function gets inlined. Only exists so to communicate that it
610
600
// doesn't matter which bit is selected; you can pick any bit without
@@ -676,39 +666,21 @@ export function markRootUpdated(
676
666
) {
677
667
root . pendingLanes |= updateLane ;
678
668
679
- // TODO: Theoretically, any update to any lane can unblock any other lane. But
680
- // it's not practical to try every single possible combination. We need a
681
- // heuristic to decide which lanes to attempt to render, and in which batches.
682
- // For now, we use the same heuristic as in the old ExpirationTimes model:
683
- // retry any lane at equal or lower priority, but don't try updates at higher
684
- // priority without also including the lower priority updates. This works well
685
- // when considering updates across different priority levels, but isn't
686
- // sufficient for updates within the same priority, since we want to treat
687
- // those updates as parallel.
688
-
689
- // Unsuspend any update at equal or lower priority.
690
- const higherPriorityLanes = updateLane - 1 ; // Turns 0b1000 into 0b0111
691
-
692
- if ( enableTransitionEntanglement ) {
693
- // If there are any suspended transitions, it's possible this new update
694
- // could unblock them. Clear the suspended lanes so that we can try rendering
695
- // them again.
696
- //
697
- // TODO: We really only need to unsuspend only lanes that are in the
698
- // `subtreeLanes` of the updated fiber, or the update lanes of the return
699
- // path. This would exclude suspended updates in an unrelated sibling tree,
700
- // since there's no way for this update to unblock it.
701
- //
702
- // We don't do this if the incoming update is idle, because we never process
703
- // idle updates until after all the regular updates have finished; there's no
704
- // way it could unblock a transition.
705
- if ( ( updateLane & IdleLanes ) === NoLanes ) {
706
- root . suspendedLanes = NoLanes ;
707
- root . pingedLanes = NoLanes ;
708
- }
709
- } else {
710
- root . suspendedLanes &= higherPriorityLanes ;
711
- root . pingedLanes &= higherPriorityLanes ;
669
+ // If there are any suspended transitions, it's possible this new update could
670
+ // unblock them. Clear the suspended lanes so that we can try rendering
671
+ // them again.
672
+ //
673
+ // TODO: We really only need to unsuspend only lanes that are in the
674
+ // `subtreeLanes` of the updated fiber, or the update lanes of the return
675
+ // path. This would exclude suspended updates in an unrelated sibling tree,
676
+ // since there's no way for this update to unblock it.
677
+ //
678
+ // We don't do this if the incoming update is idle, because we never process
679
+ // idle updates until after all the regular updates have finished; there's no
680
+ // way it could unblock a transition.
681
+ if ( ( updateLane & IdleLanes ) === NoLanes ) {
682
+ root . suspendedLanes = NoLanes ;
683
+ root . pingedLanes = NoLanes ;
712
684
}
713
685
714
686
const eventTimes = root . eventTimes ;
0 commit comments