365
}
366
}
367
368
+ // Check for entangled lanes and add them to the batch.
369
+ //
370
+ // A lane is said to be entangled with another when it's not allowed to render
371
+ // in a batch that does not also include the other lane. Typically we do this
372
+ // when multiple updates have the same source, and we only want to respond to
373
+ // the most recent event from that source.
374
+ //
375
+ // Note that we apply entanglements *after* checking for partial work above.
376
+ // This means that if a lane is entangled during an interleaved event while
377
+ // it's already rendering, we won't interrupt it. This is intentional, since
378
+ // entanglement is usually "best effort": we'll try our best to render the
379
+ // lanes in the same batch, but it's not worth throwing out partially
380
+ // completed work in order to do it.
381
+ //
382
+ // For those exceptions where entanglement is semantically important, like
383
+ // useMutableSource, we should ensure that there is no partial work at the
384
+ // time we apply the entanglement.
385
+ const entangledLanes = root.entangledLanes;
386
+ if (entangledLanes !== NoLanes) {
387
+ const entanglements = root.entanglements;
388
+ let lanes = nextLanes & entangledLanes;
389
+ while (lanes > 0) {
390
+ const index = pickArbitraryLaneIndex(lanes);
391
+ const lane = 1 << index;
392
+
393
+ nextLanes |= entanglements[index];
394
+
395
+ lanes &= ~lane;
396
+ }
397
+ }
398
+
399
return nextLanes;
400
}
401
432
// it as expired to force it to finish.
433
let lanes = pendingLanes;
434
while (lanes > 0) {
404
- const index = ctrz(lanes);
435
+ const index = pickArbitraryLaneIndex(lanes);
436
const lane = 1 << index;
437
438
const expirationTime = expirationTimes[index];
619
return getLowestPriorityLane(lanes);
620
}
621
622
+function pickArbitraryLaneIndex(lanes: Lane | Lanes) {
623
+ return 31 - clz32(lanes);
624
+}
625
+
626
export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane) {
627
return (a & b) !== NoLanes;
628
}
682
const expirationTimes = root.expirationTimes;
683
let lanes = suspendedLanes;
684
while (lanes > 0) {
650
- const index = ctrz(lanes);
685
+ const index = pickArbitraryLaneIndex(lanes);
686
const lane = 1 << index;
687
688
expirationTimes[index] = NoTimestamp;
727
root.expiredLanes &= remainingLanes;
728
root.mutableReadLanes &= remainingLanes;
729
730
+ root.entangledLanes &= remainingLanes;
731
+
732
const expirationTimes = root.expirationTimes;
733
let lanes = noLongerPendingLanes;
734
while (lanes > 0) {
698
- const index = ctrz(lanes);
735
+ const index = pickArbitraryLaneIndex(lanes);
736
const lane = 1 << index;
737
738
// Clear the expiration time
742
}
743
}
744
745
+export function markRootEntangled(root: FiberRoot, entangledLanes: Lanes) {
746
+ root.entangledLanes |= entangledLanes;
747
+
748
+ const entanglements = root.entanglements;
749
+ let lanes = entangledLanes;
750
+ while (lanes > 0) {
751
+ const index = pickArbitraryLaneIndex(lanes);
752
+ const lane = 1 << index;
753
+
754
+ entanglements[index] |= entangledLanes;
755
+
756
+ lanes &= ~lane;
757
+ }
758
+}
759
+
760
export function getBumpedLaneForHydration(
761
root: FiberRoot,
762
renderLanes: Lanes,
829
}
830
return (31 - ((log(lanes) / LN2) | 0)) | 0;
831
}
780
-
781
-// Count trailing zeros. Only used on lanes, so assume input is an integer.
782
-function ctrz(lanes: Lanes | Lane) {
783
- let bits = lanes;
784
- bits |= bits << 16;
785
- bits |= bits << 8;
786
- bits |= bits << 4;
787
- bits |= bits << 2;
788
- bits |= bits << 1;
789
- return 32 - clz32(~bits);
790
-}