1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {Fiber, FiberRoot} from './ReactInternalTypes';
    
  11. import type {Transition} from './ReactFiberTracingMarkerComponent';
    
  12. import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates';
    
  13. 
    
  14. // TODO: Ideally these types would be opaque but that doesn't work well with
    
  15. // our reconciler fork infra, since these leak into non-reconciler packages.
    
  16. 
    
  17. export type Lanes = number;
    
  18. export type Lane = number;
    
  19. export type LaneMap<T> = Array<T>;
    
  20. 
    
  21. import {
    
  22.   enableSchedulingProfiler,
    
  23.   enableUpdaterTracking,
    
  24.   allowConcurrentByDefault,
    
  25.   enableTransitionTracing,
    
  26.   enableUnifiedSyncLane,
    
  27. } from 'shared/ReactFeatureFlags';
    
  28. import {isDevToolsPresent} from './ReactFiberDevToolsHook';
    
  29. import {ConcurrentUpdatesByDefaultMode, NoMode} from './ReactTypeOfMode';
    
  30. import {clz32} from './clz32';
    
  31. 
    
  32. // Lane values below should be kept in sync with getLabelForLane(), used by react-devtools-timeline.
    
  33. // If those values are changed that package should be rebuilt and redeployed.
    
  34. 
    
  35. export const TotalLanes = 31;
    
  36. 
    
  37. export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
    
  38. export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;
    
  39. 
    
  40. export const SyncHydrationLane: Lane = /*               */ 0b0000000000000000000000000000001;
    
  41. export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000010;
    
  42. export const SyncLaneIndex: number = 1;
    
  43. 
    
  44. export const InputContinuousHydrationLane: Lane = /*    */ 0b0000000000000000000000000000100;
    
  45. export const InputContinuousLane: Lane = /*             */ 0b0000000000000000000000000001000;
    
  46. 
    
  47. export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000000010000;
    
  48. export const DefaultLane: Lane = /*                     */ 0b0000000000000000000000000100000;
    
  49. 
    
  50. export const SyncUpdateLanes: Lane = enableUnifiedSyncLane
    
  51.   ? SyncLane | InputContinuousLane | DefaultLane
    
  52.   : SyncLane;
    
  53. 
    
  54. const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000000000001000000;
    
  55. const TransitionLanes: Lanes = /*                       */ 0b0000000001111111111111110000000;
    
  56. const TransitionLane1: Lane = /*                        */ 0b0000000000000000000000010000000;
    
  57. const TransitionLane2: Lane = /*                        */ 0b0000000000000000000000100000000;
    
  58. const TransitionLane3: Lane = /*                        */ 0b0000000000000000000001000000000;
    
  59. const TransitionLane4: Lane = /*                        */ 0b0000000000000000000010000000000;
    
  60. const TransitionLane5: Lane = /*                        */ 0b0000000000000000000100000000000;
    
  61. const TransitionLane6: Lane = /*                        */ 0b0000000000000000001000000000000;
    
  62. const TransitionLane7: Lane = /*                        */ 0b0000000000000000010000000000000;
    
  63. const TransitionLane8: Lane = /*                        */ 0b0000000000000000100000000000000;
    
  64. const TransitionLane9: Lane = /*                        */ 0b0000000000000001000000000000000;
    
  65. const TransitionLane10: Lane = /*                       */ 0b0000000000000010000000000000000;
    
  66. const TransitionLane11: Lane = /*                       */ 0b0000000000000100000000000000000;
    
  67. const TransitionLane12: Lane = /*                       */ 0b0000000000001000000000000000000;
    
  68. const TransitionLane13: Lane = /*                       */ 0b0000000000010000000000000000000;
    
  69. const TransitionLane14: Lane = /*                       */ 0b0000000000100000000000000000000;
    
  70. const TransitionLane15: Lane = /*                       */ 0b0000000001000000000000000000000;
    
  71. 
    
  72. const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;
    
  73. const RetryLane1: Lane = /*                             */ 0b0000000010000000000000000000000;
    
  74. const RetryLane2: Lane = /*                             */ 0b0000000100000000000000000000000;
    
  75. const RetryLane3: Lane = /*                             */ 0b0000001000000000000000000000000;
    
  76. const RetryLane4: Lane = /*                             */ 0b0000010000000000000000000000000;
    
  77. 
    
  78. export const SomeRetryLane: Lane = RetryLane1;
    
  79. 
    
  80. export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;
    
  81. 
    
  82. const NonIdleLanes: Lanes = /*                          */ 0b0000111111111111111111111111111;
    
  83. 
    
  84. export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
    
  85. export const IdleLane: Lane = /*                        */ 0b0010000000000000000000000000000;
    
  86. 
    
  87. export const OffscreenLane: Lane = /*                   */ 0b0100000000000000000000000000000;
    
  88. export const DeferredLane: Lane = /*                    */ 0b1000000000000000000000000000000;
    
  89. 
    
  90. // Any lane that might schedule an update. This is used to detect infinite
    
  91. // update loops, so it doesn't include hydration lanes or retries.
    
  92. export const UpdateLanes: Lanes =
    
  93.   SyncLane | InputContinuousLane | DefaultLane | TransitionLanes;
    
  94. 
    
  95. // This function is used for the experimental timeline (react-devtools-timeline)
    
  96. // It should be kept in sync with the Lanes values above.
    
  97. export function getLabelForLane(lane: Lane): string | void {
    
  98.   if (enableSchedulingProfiler) {
    
  99.     if (lane & SyncHydrationLane) {
    
  100.       return 'SyncHydrationLane';
    
  101.     }
    
  102.     if (lane & SyncLane) {
    
  103.       return 'Sync';
    
  104.     }
    
  105.     if (lane & InputContinuousHydrationLane) {
    
  106.       return 'InputContinuousHydration';
    
  107.     }
    
  108.     if (lane & InputContinuousLane) {
    
  109.       return 'InputContinuous';
    
  110.     }
    
  111.     if (lane & DefaultHydrationLane) {
    
  112.       return 'DefaultHydration';
    
  113.     }
    
  114.     if (lane & DefaultLane) {
    
  115.       return 'Default';
    
  116.     }
    
  117.     if (lane & TransitionHydrationLane) {
    
  118.       return 'TransitionHydration';
    
  119.     }
    
  120.     if (lane & TransitionLanes) {
    
  121.       return 'Transition';
    
  122.     }
    
  123.     if (lane & RetryLanes) {
    
  124.       return 'Retry';
    
  125.     }
    
  126.     if (lane & SelectiveHydrationLane) {
    
  127.       return 'SelectiveHydration';
    
  128.     }
    
  129.     if (lane & IdleHydrationLane) {
    
  130.       return 'IdleHydration';
    
  131.     }
    
  132.     if (lane & IdleLane) {
    
  133.       return 'Idle';
    
  134.     }
    
  135.     if (lane & OffscreenLane) {
    
  136.       return 'Offscreen';
    
  137.     }
    
  138.     if (lane & DeferredLane) {
    
  139.       return 'Deferred';
    
  140.     }
    
  141.   }
    
  142. }
    
  143. 
    
  144. export const NoTimestamp = -1;
    
  145. 
    
  146. let nextTransitionLane: Lane = TransitionLane1;
    
  147. let nextRetryLane: Lane = RetryLane1;
    
  148. 
    
  149. function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
    
  150.   if (enableUnifiedSyncLane) {
    
  151.     const pendingSyncLanes = lanes & SyncUpdateLanes;
    
  152.     if (pendingSyncLanes !== 0) {
    
  153.       return pendingSyncLanes;
    
  154.     }
    
  155.   }
    
  156.   switch (getHighestPriorityLane(lanes)) {
    
  157.     case SyncHydrationLane:
    
  158.       return SyncHydrationLane;
    
  159.     case SyncLane:
    
  160.       return SyncLane;
    
  161.     case InputContinuousHydrationLane:
    
  162.       return InputContinuousHydrationLane;
    
  163.     case InputContinuousLane:
    
  164.       return InputContinuousLane;
    
  165.     case DefaultHydrationLane:
    
  166.       return DefaultHydrationLane;
    
  167.     case DefaultLane:
    
  168.       return DefaultLane;
    
  169.     case TransitionHydrationLane:
    
  170.       return TransitionHydrationLane;
    
  171.     case TransitionLane1:
    
  172.     case TransitionLane2:
    
  173.     case TransitionLane3:
    
  174.     case TransitionLane4:
    
  175.     case TransitionLane5:
    
  176.     case TransitionLane6:
    
  177.     case TransitionLane7:
    
  178.     case TransitionLane8:
    
  179.     case TransitionLane9:
    
  180.     case TransitionLane10:
    
  181.     case TransitionLane11:
    
  182.     case TransitionLane12:
    
  183.     case TransitionLane13:
    
  184.     case TransitionLane14:
    
  185.     case TransitionLane15:
    
  186.       return lanes & TransitionLanes;
    
  187.     case RetryLane1:
    
  188.     case RetryLane2:
    
  189.     case RetryLane3:
    
  190.     case RetryLane4:
    
  191.       return lanes & RetryLanes;
    
  192.     case SelectiveHydrationLane:
    
  193.       return SelectiveHydrationLane;
    
  194.     case IdleHydrationLane:
    
  195.       return IdleHydrationLane;
    
  196.     case IdleLane:
    
  197.       return IdleLane;
    
  198.     case OffscreenLane:
    
  199.       return OffscreenLane;
    
  200.     case DeferredLane:
    
  201.       // This shouldn't be reachable because deferred work is always entangled
    
  202.       // with something else.
    
  203.       return NoLanes;
    
  204.     default:
    
  205.       if (__DEV__) {
    
  206.         console.error(
    
  207.           'Should have found matching lanes. This is a bug in React.',
    
  208.         );
    
  209.       }
    
  210.       // This shouldn't be reachable, but as a fallback, return the entire bitmask.
    
  211.       return lanes;
    
  212.   }
    
  213. }
    
  214. 
    
  215. export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
    
  216.   // Early bailout if there's no pending work left.
    
  217.   const pendingLanes = root.pendingLanes;
    
  218.   if (pendingLanes === NoLanes) {
    
  219.     return NoLanes;
    
  220.   }
    
  221. 
    
  222.   let nextLanes = NoLanes;
    
  223. 
    
  224.   const suspendedLanes = root.suspendedLanes;
    
  225.   const pingedLanes = root.pingedLanes;
    
  226. 
    
  227.   // Do not work on any idle work until all the non-idle work has finished,
    
  228.   // even if the work is suspended.
    
  229.   const nonIdlePendingLanes = pendingLanes & NonIdleLanes;
    
  230.   if (nonIdlePendingLanes !== NoLanes) {
    
  231.     const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
    
  232.     if (nonIdleUnblockedLanes !== NoLanes) {
    
  233.       nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
    
  234.     } else {
    
  235.       const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
    
  236.       if (nonIdlePingedLanes !== NoLanes) {
    
  237.         nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
    
  238.       }
    
  239.     }
    
  240.   } else {
    
  241.     // The only remaining work is Idle.
    
  242.     const unblockedLanes = pendingLanes & ~suspendedLanes;
    
  243.     if (unblockedLanes !== NoLanes) {
    
  244.       nextLanes = getHighestPriorityLanes(unblockedLanes);
    
  245.     } else {
    
  246.       if (pingedLanes !== NoLanes) {
    
  247.         nextLanes = getHighestPriorityLanes(pingedLanes);
    
  248.       }
    
  249.     }
    
  250.   }
    
  251. 
    
  252.   if (nextLanes === NoLanes) {
    
  253.     // This should only be reachable if we're suspended
    
  254.     // TODO: Consider warning in this path if a fallback timer is not scheduled.
    
  255.     return NoLanes;
    
  256.   }
    
  257. 
    
  258.   // If we're already in the middle of a render, switching lanes will interrupt
    
  259.   // it and we'll lose our progress. We should only do this if the new lanes are
    
  260.   // higher priority.
    
  261.   if (
    
  262.     wipLanes !== NoLanes &&
    
  263.     wipLanes !== nextLanes &&
    
  264.     // If we already suspended with a delay, then interrupting is fine. Don't
    
  265.     // bother waiting until the root is complete.
    
  266.     (wipLanes & suspendedLanes) === NoLanes
    
  267.   ) {
    
  268.     const nextLane = getHighestPriorityLane(nextLanes);
    
  269.     const wipLane = getHighestPriorityLane(wipLanes);
    
  270.     if (
    
  271.       // Tests whether the next lane is equal or lower priority than the wip
    
  272.       // one. This works because the bits decrease in priority as you go left.
    
  273.       nextLane >= wipLane ||
    
  274.       // Default priority updates should not interrupt transition updates. The
    
  275.       // only difference between default updates and transition updates is that
    
  276.       // default updates do not support refresh transitions.
    
  277.       (nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)
    
  278.     ) {
    
  279.       // Keep working on the existing in-progress tree. Do not interrupt.
    
  280.       return wipLanes;
    
  281.     }
    
  282.   }
    
  283. 
    
  284.   return nextLanes;
    
  285. }
    
  286. 
    
  287. export function getEntangledLanes(root: FiberRoot, renderLanes: Lanes): Lanes {
    
  288.   let entangledLanes = renderLanes;
    
  289. 
    
  290.   if (
    
  291.     allowConcurrentByDefault &&
    
  292.     (root.current.mode & ConcurrentUpdatesByDefaultMode) !== NoMode
    
  293.   ) {
    
  294.     // Do nothing, use the lanes as they were assigned.
    
  295.   } else if ((entangledLanes & InputContinuousLane) !== NoLanes) {
    
  296.     // When updates are sync by default, we entangle continuous priority updates
    
  297.     // and default updates, so they render in the same batch. The only reason
    
  298.     // they use separate lanes is because continuous updates should interrupt
    
  299.     // transitions, but default updates should not.
    
  300.     entangledLanes |= entangledLanes & DefaultLane;
    
  301.   }
    
  302. 
    
  303.   // Check for entangled lanes and add them to the batch.
    
  304.   //
    
  305.   // A lane is said to be entangled with another when it's not allowed to render
    
  306.   // in a batch that does not also include the other lane. Typically we do this
    
  307.   // when multiple updates have the same source, and we only want to respond to
    
  308.   // the most recent event from that source.
    
  309.   //
    
  310.   // Note that we apply entanglements *after* checking for partial work above.
    
  311.   // This means that if a lane is entangled during an interleaved event while
    
  312.   // it's already rendering, we won't interrupt it. This is intentional, since
    
  313.   // entanglement is usually "best effort": we'll try our best to render the
    
  314.   // lanes in the same batch, but it's not worth throwing out partially
    
  315.   // completed work in order to do it.
    
  316.   // TODO: Reconsider this. The counter-argument is that the partial work
    
  317.   // represents an intermediate state, which we don't want to show to the user.
    
  318.   // And by spending extra time finishing it, we're increasing the amount of
    
  319.   // time it takes to show the final state, which is what they are actually
    
  320.   // waiting for.
    
  321.   //
    
  322.   // For those exceptions where entanglement is semantically important,
    
  323.   // we should ensure that there is no partial work at the
    
  324.   // time we apply the entanglement.
    
  325.   const allEntangledLanes = root.entangledLanes;
    
  326.   if (allEntangledLanes !== NoLanes) {
    
  327.     const entanglements = root.entanglements;
    
  328.     let lanes = entangledLanes & allEntangledLanes;
    
  329.     while (lanes > 0) {
    
  330.       const index = pickArbitraryLaneIndex(lanes);
    
  331.       const lane = 1 << index;
    
  332. 
    
  333.       entangledLanes |= entanglements[index];
    
  334. 
    
  335.       lanes &= ~lane;
    
  336.     }
    
  337.   }
    
  338. 
    
  339.   return entangledLanes;
    
  340. }
    
  341. 
    
  342. function computeExpirationTime(lane: Lane, currentTime: number) {
    
  343.   switch (lane) {
    
  344.     case SyncHydrationLane:
    
  345.     case SyncLane:
    
  346.     case InputContinuousHydrationLane:
    
  347.     case InputContinuousLane:
    
  348.       // User interactions should expire slightly more quickly.
    
  349.       //
    
  350.       // NOTE: This is set to the corresponding constant as in Scheduler.js.
    
  351.       // When we made it larger, a product metric in www regressed, suggesting
    
  352.       // there's a user interaction that's being starved by a series of
    
  353.       // synchronous updates. If that theory is correct, the proper solution is
    
  354.       // to fix the starvation. However, this scenario supports the idea that
    
  355.       // expiration times are an important safeguard when starvation
    
  356.       // does happen.
    
  357.       return currentTime + 250;
    
  358.     case DefaultHydrationLane:
    
  359.     case DefaultLane:
    
  360.     case TransitionHydrationLane:
    
  361.     case TransitionLane1:
    
  362.     case TransitionLane2:
    
  363.     case TransitionLane3:
    
  364.     case TransitionLane4:
    
  365.     case TransitionLane5:
    
  366.     case TransitionLane6:
    
  367.     case TransitionLane7:
    
  368.     case TransitionLane8:
    
  369.     case TransitionLane9:
    
  370.     case TransitionLane10:
    
  371.     case TransitionLane11:
    
  372.     case TransitionLane12:
    
  373.     case TransitionLane13:
    
  374.     case TransitionLane14:
    
  375.     case TransitionLane15:
    
  376.       return currentTime + 5000;
    
  377.     case RetryLane1:
    
  378.     case RetryLane2:
    
  379.     case RetryLane3:
    
  380.     case RetryLane4:
    
  381.       // TODO: Retries should be allowed to expire if they are CPU bound for
    
  382.       // too long, but when I made this change it caused a spike in browser
    
  383.       // crashes. There must be some other underlying bug; not super urgent but
    
  384.       // ideally should figure out why and fix it. Unfortunately we don't have
    
  385.       // a repro for the crashes, only detected via production metrics.
    
  386.       return NoTimestamp;
    
  387.     case SelectiveHydrationLane:
    
  388.     case IdleHydrationLane:
    
  389.     case IdleLane:
    
  390.     case OffscreenLane:
    
  391.     case DeferredLane:
    
  392.       // Anything idle priority or lower should never expire.
    
  393.       return NoTimestamp;
    
  394.     default:
    
  395.       if (__DEV__) {
    
  396.         console.error(
    
  397.           'Should have found matching lanes. This is a bug in React.',
    
  398.         );
    
  399.       }
    
  400.       return NoTimestamp;
    
  401.   }
    
  402. }
    
  403. 
    
  404. export function markStarvedLanesAsExpired(
    
  405.   root: FiberRoot,
    
  406.   currentTime: number,
    
  407. ): void {
    
  408.   // TODO: This gets called every time we yield. We can optimize by storing
    
  409.   // the earliest expiration time on the root. Then use that to quickly bail out
    
  410.   // of this function.
    
  411. 
    
  412.   const pendingLanes = root.pendingLanes;
    
  413.   const suspendedLanes = root.suspendedLanes;
    
  414.   const pingedLanes = root.pingedLanes;
    
  415.   const expirationTimes = root.expirationTimes;
    
  416. 
    
  417.   // Iterate through the pending lanes and check if we've reached their
    
  418.   // expiration time. If so, we'll assume the update is being starved and mark
    
  419.   // it as expired to force it to finish.
    
  420.   // TODO: We should be able to replace this with upgradePendingLanesToSync
    
  421.   //
    
  422.   // We exclude retry lanes because those must always be time sliced, in order
    
  423.   // to unwrap uncached promises.
    
  424.   // TODO: Write a test for this
    
  425.   let lanes = pendingLanes & ~RetryLanes;
    
  426.   while (lanes > 0) {
    
  427.     const index = pickArbitraryLaneIndex(lanes);
    
  428.     const lane = 1 << index;
    
  429. 
    
  430.     const expirationTime = expirationTimes[index];
    
  431.     if (expirationTime === NoTimestamp) {
    
  432.       // Found a pending lane with no expiration time. If it's not suspended, or
    
  433.       // if it's pinged, assume it's CPU-bound. Compute a new expiration time
    
  434.       // using the current time.
    
  435.       if (
    
  436.         (lane & suspendedLanes) === NoLanes ||
    
  437.         (lane & pingedLanes) !== NoLanes
    
  438.       ) {
    
  439.         // Assumes timestamps are monotonically increasing.
    
  440.         expirationTimes[index] = computeExpirationTime(lane, currentTime);
    
  441.       }
    
  442.     } else if (expirationTime <= currentTime) {
    
  443.       // This lane expired
    
  444.       root.expiredLanes |= lane;
    
  445.     }
    
  446. 
    
  447.     lanes &= ~lane;
    
  448.   }
    
  449. }
    
  450. 
    
  451. // This returns the highest priority pending lanes regardless of whether they
    
  452. // are suspended.
    
  453. export function getHighestPriorityPendingLanes(root: FiberRoot): Lanes {
    
  454.   return getHighestPriorityLanes(root.pendingLanes);
    
  455. }
    
  456. 
    
  457. export function getLanesToRetrySynchronouslyOnError(
    
  458.   root: FiberRoot,
    
  459.   originallyAttemptedLanes: Lanes,
    
  460. ): Lanes {
    
  461.   if (root.errorRecoveryDisabledLanes & originallyAttemptedLanes) {
    
  462.     // The error recovery mechanism is disabled until these lanes are cleared.
    
  463.     return NoLanes;
    
  464.   }
    
  465. 
    
  466.   const everythingButOffscreen = root.pendingLanes & ~OffscreenLane;
    
  467.   if (everythingButOffscreen !== NoLanes) {
    
  468.     return everythingButOffscreen;
    
  469.   }
    
  470.   if (everythingButOffscreen & OffscreenLane) {
    
  471.     return OffscreenLane;
    
  472.   }
    
  473.   return NoLanes;
    
  474. }
    
  475. 
    
  476. export function includesSyncLane(lanes: Lanes): boolean {
    
  477.   return (lanes & (SyncLane | SyncHydrationLane)) !== NoLanes;
    
  478. }
    
  479. 
    
  480. export function includesNonIdleWork(lanes: Lanes): boolean {
    
  481.   return (lanes & NonIdleLanes) !== NoLanes;
    
  482. }
    
  483. export function includesOnlyRetries(lanes: Lanes): boolean {
    
  484.   return (lanes & RetryLanes) === lanes;
    
  485. }
    
  486. export function includesOnlyNonUrgentLanes(lanes: Lanes): boolean {
    
  487.   // TODO: Should hydration lanes be included here? This function is only
    
  488.   // used in `updateDeferredValueImpl`.
    
  489.   const UrgentLanes = SyncLane | InputContinuousLane | DefaultLane;
    
  490.   return (lanes & UrgentLanes) === NoLanes;
    
  491. }
    
  492. export function includesOnlyTransitions(lanes: Lanes): boolean {
    
  493.   return (lanes & TransitionLanes) === lanes;
    
  494. }
    
  495. 
    
  496. export function includesBlockingLane(root: FiberRoot, lanes: Lanes): boolean {
    
  497.   if (
    
  498.     allowConcurrentByDefault &&
    
  499.     (root.current.mode & ConcurrentUpdatesByDefaultMode) !== NoMode
    
  500.   ) {
    
  501.     // Concurrent updates by default always use time slicing.
    
  502.     return false;
    
  503.   }
    
  504.   const SyncDefaultLanes =
    
  505.     InputContinuousHydrationLane |
    
  506.     InputContinuousLane |
    
  507.     DefaultHydrationLane |
    
  508.     DefaultLane;
    
  509.   return (lanes & SyncDefaultLanes) !== NoLanes;
    
  510. }
    
  511. 
    
  512. export function includesExpiredLane(root: FiberRoot, lanes: Lanes): boolean {
    
  513.   // This is a separate check from includesBlockingLane because a lane can
    
  514.   // expire after a render has already started.
    
  515.   return (lanes & root.expiredLanes) !== NoLanes;
    
  516. }
    
  517. 
    
  518. export function isTransitionLane(lane: Lane): boolean {
    
  519.   return (lane & TransitionLanes) !== NoLanes;
    
  520. }
    
  521. 
    
  522. export function claimNextTransitionLane(): Lane {
    
  523.   // Cycle through the lanes, assigning each new transition to the next lane.
    
  524.   // In most cases, this means every transition gets its own lane, until we
    
  525.   // run out of lanes and cycle back to the beginning.
    
  526.   const lane = nextTransitionLane;
    
  527.   nextTransitionLane <<= 1;
    
  528.   if ((nextTransitionLane & TransitionLanes) === NoLanes) {
    
  529.     nextTransitionLane = TransitionLane1;
    
  530.   }
    
  531.   return lane;
    
  532. }
    
  533. 
    
  534. export function claimNextRetryLane(): Lane {
    
  535.   const lane = nextRetryLane;
    
  536.   nextRetryLane <<= 1;
    
  537.   if ((nextRetryLane & RetryLanes) === NoLanes) {
    
  538.     nextRetryLane = RetryLane1;
    
  539.   }
    
  540.   return lane;
    
  541. }
    
  542. 
    
  543. export function getHighestPriorityLane(lanes: Lanes): Lane {
    
  544.   return lanes & -lanes;
    
  545. }
    
  546. 
    
  547. export function pickArbitraryLane(lanes: Lanes): Lane {
    
  548.   // This wrapper function gets inlined. Only exists so to communicate that it
    
  549.   // doesn't matter which bit is selected; you can pick any bit without
    
  550.   // affecting the algorithms where its used. Here I'm using
    
  551.   // getHighestPriorityLane because it requires the fewest operations.
    
  552.   return getHighestPriorityLane(lanes);
    
  553. }
    
  554. 
    
  555. function pickArbitraryLaneIndex(lanes: Lanes) {
    
  556.   return 31 - clz32(lanes);
    
  557. }
    
  558. 
    
  559. function laneToIndex(lane: Lane) {
    
  560.   return pickArbitraryLaneIndex(lane);
    
  561. }
    
  562. 
    
  563. export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane): boolean {
    
  564.   return (a & b) !== NoLanes;
    
  565. }
    
  566. 
    
  567. export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane): boolean {
    
  568.   return (set & subset) === subset;
    
  569. }
    
  570. 
    
  571. export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
    
  572.   return a | b;
    
  573. }
    
  574. 
    
  575. export function removeLanes(set: Lanes, subset: Lanes | Lane): Lanes {
    
  576.   return set & ~subset;
    
  577. }
    
  578. 
    
  579. export function intersectLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
    
  580.   return a & b;
    
  581. }
    
  582. 
    
  583. // Seems redundant, but it changes the type from a single lane (used for
    
  584. // updates) to a group of lanes (used for flushing work).
    
  585. export function laneToLanes(lane: Lane): Lanes {
    
  586.   return lane;
    
  587. }
    
  588. 
    
  589. export function higherPriorityLane(a: Lane, b: Lane): Lane {
    
  590.   // This works because the bit ranges decrease in priority as you go left.
    
  591.   return a !== NoLane && a < b ? a : b;
    
  592. }
    
  593. 
    
  594. export function createLaneMap<T>(initial: T): LaneMap<T> {
    
  595.   // Intentionally pushing one by one.
    
  596.   // https://v8.dev/blog/elements-kinds#avoid-creating-holes
    
  597.   const laneMap = [];
    
  598.   for (let i = 0; i < TotalLanes; i++) {
    
  599.     laneMap.push(initial);
    
  600.   }
    
  601.   return laneMap;
    
  602. }
    
  603. 
    
  604. export function markRootUpdated(root: FiberRoot, updateLane: Lane) {
    
  605.   root.pendingLanes |= updateLane;
    
  606. 
    
  607.   // If there are any suspended transitions, it's possible this new update
    
  608.   // could unblock them. Clear the suspended lanes so that we can try rendering
    
  609.   // them again.
    
  610.   //
    
  611.   // TODO: We really only need to unsuspend only lanes that are in the
    
  612.   // `subtreeLanes` of the updated fiber, or the update lanes of the return
    
  613.   // path. This would exclude suspended updates in an unrelated sibling tree,
    
  614.   // since there's no way for this update to unblock it.
    
  615.   //
    
  616.   // We don't do this if the incoming update is idle, because we never process
    
  617.   // idle updates until after all the regular updates have finished; there's no
    
  618.   // way it could unblock a transition.
    
  619.   if (updateLane !== IdleLane) {
    
  620.     root.suspendedLanes = NoLanes;
    
  621.     root.pingedLanes = NoLanes;
    
  622.   }
    
  623. }
    
  624. 
    
  625. export function markRootSuspended(
    
  626.   root: FiberRoot,
    
  627.   suspendedLanes: Lanes,
    
  628.   spawnedLane: Lane,
    
  629. ) {
    
  630.   root.suspendedLanes |= suspendedLanes;
    
  631.   root.pingedLanes &= ~suspendedLanes;
    
  632. 
    
  633.   // The suspended lanes are no longer CPU-bound. Clear their expiration times.
    
  634.   const expirationTimes = root.expirationTimes;
    
  635.   let lanes = suspendedLanes;
    
  636.   while (lanes > 0) {
    
  637.     const index = pickArbitraryLaneIndex(lanes);
    
  638.     const lane = 1 << index;
    
  639. 
    
  640.     expirationTimes[index] = NoTimestamp;
    
  641. 
    
  642.     lanes &= ~lane;
    
  643.   }
    
  644. 
    
  645.   if (spawnedLane !== NoLane) {
    
  646.     markSpawnedDeferredLane(root, spawnedLane, suspendedLanes);
    
  647.   }
    
  648. }
    
  649. 
    
  650. export function markRootPinged(root: FiberRoot, pingedLanes: Lanes) {
    
  651.   root.pingedLanes |= root.suspendedLanes & pingedLanes;
    
  652. }
    
  653. 
    
  654. export function markRootFinished(
    
  655.   root: FiberRoot,
    
  656.   remainingLanes: Lanes,
    
  657.   spawnedLane: Lane,
    
  658. ) {
    
  659.   const noLongerPendingLanes = root.pendingLanes & ~remainingLanes;
    
  660. 
    
  661.   root.pendingLanes = remainingLanes;
    
  662. 
    
  663.   // Let's try everything again
    
  664.   root.suspendedLanes = NoLanes;
    
  665.   root.pingedLanes = NoLanes;
    
  666. 
    
  667.   root.expiredLanes &= remainingLanes;
    
  668. 
    
  669.   root.entangledLanes &= remainingLanes;
    
  670. 
    
  671.   root.errorRecoveryDisabledLanes &= remainingLanes;
    
  672.   root.shellSuspendCounter = 0;
    
  673. 
    
  674.   const entanglements = root.entanglements;
    
  675.   const expirationTimes = root.expirationTimes;
    
  676.   const hiddenUpdates = root.hiddenUpdates;
    
  677. 
    
  678.   // Clear the lanes that no longer have pending work
    
  679.   let lanes = noLongerPendingLanes;
    
  680.   while (lanes > 0) {
    
  681.     const index = pickArbitraryLaneIndex(lanes);
    
  682.     const lane = 1 << index;
    
  683. 
    
  684.     entanglements[index] = NoLanes;
    
  685.     expirationTimes[index] = NoTimestamp;
    
  686. 
    
  687.     const hiddenUpdatesForLane = hiddenUpdates[index];
    
  688.     if (hiddenUpdatesForLane !== null) {
    
  689.       hiddenUpdates[index] = null;
    
  690.       // "Hidden" updates are updates that were made to a hidden component. They
    
  691.       // have special logic associated with them because they may be entangled
    
  692.       // with updates that occur outside that tree. But once the outer tree
    
  693.       // commits, they behave like regular updates.
    
  694.       for (let i = 0; i < hiddenUpdatesForLane.length; i++) {
    
  695.         const update = hiddenUpdatesForLane[i];
    
  696.         if (update !== null) {
    
  697.           update.lane &= ~OffscreenLane;
    
  698.         }
    
  699.       }
    
  700.     }
    
  701. 
    
  702.     lanes &= ~lane;
    
  703.   }
    
  704. 
    
  705.   if (spawnedLane !== NoLane) {
    
  706.     markSpawnedDeferredLane(
    
  707.       root,
    
  708.       spawnedLane,
    
  709.       // This render finished successfully without suspending, so we don't need
    
  710.       // to entangle the spawned task with the parent task.
    
  711.       NoLanes,
    
  712.     );
    
  713.   }
    
  714. }
    
  715. 
    
  716. function markSpawnedDeferredLane(
    
  717.   root: FiberRoot,
    
  718.   spawnedLane: Lane,
    
  719.   entangledLanes: Lanes,
    
  720. ) {
    
  721.   // This render spawned a deferred task. Mark it as pending.
    
  722.   root.pendingLanes |= spawnedLane;
    
  723.   root.suspendedLanes &= ~spawnedLane;
    
  724. 
    
  725.   // Entangle the spawned lane with the DeferredLane bit so that we know it
    
  726.   // was the result of another render. This lets us avoid a useDeferredValue
    
  727.   // waterfall — only the first level will defer.
    
  728.   const spawnedLaneIndex = laneToIndex(spawnedLane);
    
  729.   root.entangledLanes |= spawnedLane;
    
  730.   root.entanglements[spawnedLaneIndex] |=
    
  731.     DeferredLane |
    
  732.     // If the parent render task suspended, we must also entangle those lanes
    
  733.     // with the spawned task, so that the deferred task includes all the same
    
  734.     // updates that the parent task did. We can exclude any lane that is not
    
  735.     // used for updates (e.g. Offscreen).
    
  736.     (entangledLanes & UpdateLanes);
    
  737. }
    
  738. 
    
  739. export function markRootEntangled(root: FiberRoot, entangledLanes: Lanes) {
    
  740.   // In addition to entangling each of the given lanes with each other, we also
    
  741.   // have to consider _transitive_ entanglements. For each lane that is already
    
  742.   // entangled with *any* of the given lanes, that lane is now transitively
    
  743.   // entangled with *all* the given lanes.
    
  744.   //
    
  745.   // Translated: If C is entangled with A, then entangling A with B also
    
  746.   // entangles C with B.
    
  747.   //
    
  748.   // If this is hard to grasp, it might help to intentionally break this
    
  749.   // function and look at the tests that fail in ReactTransition-test.js. Try
    
  750.   // commenting out one of the conditions below.
    
  751. 
    
  752.   const rootEntangledLanes = (root.entangledLanes |= entangledLanes);
    
  753.   const entanglements = root.entanglements;
    
  754.   let lanes = rootEntangledLanes;
    
  755.   while (lanes) {
    
  756.     const index = pickArbitraryLaneIndex(lanes);
    
  757.     const lane = 1 << index;
    
  758.     if (
    
  759.       // Is this one of the newly entangled lanes?
    
  760.       (lane & entangledLanes) |
    
  761.       // Is this lane transitively entangled with the newly entangled lanes?
    
  762.       (entanglements[index] & entangledLanes)
    
  763.     ) {
    
  764.       entanglements[index] |= entangledLanes;
    
  765.     }
    
  766.     lanes &= ~lane;
    
  767.   }
    
  768. }
    
  769. 
    
  770. export function upgradePendingLaneToSync(root: FiberRoot, lane: Lane) {
    
  771.   // Since we're upgrading the priority of the given lane, there is now pending
    
  772.   // sync work.
    
  773.   root.pendingLanes |= SyncLane;
    
  774. 
    
  775.   // Entangle the sync lane with the lane we're upgrading. This means SyncLane
    
  776.   // will not be allowed to finish without also finishing the given lane.
    
  777.   root.entangledLanes |= SyncLane;
    
  778.   root.entanglements[SyncLaneIndex] |= lane;
    
  779. }
    
  780. 
    
  781. export function upgradePendingLanesToSync(
    
  782.   root: FiberRoot,
    
  783.   lanesToUpgrade: Lanes,
    
  784. ) {
    
  785.   // Same as upgradePendingLaneToSync but accepts multiple lanes, so it's a
    
  786.   // bit slower.
    
  787.   root.pendingLanes |= SyncLane;
    
  788.   root.entangledLanes |= SyncLane;
    
  789.   let lanes = lanesToUpgrade;
    
  790.   while (lanes) {
    
  791.     const index = pickArbitraryLaneIndex(lanes);
    
  792.     const lane = 1 << index;
    
  793.     root.entanglements[SyncLaneIndex] |= lane;
    
  794.     lanes &= ~lane;
    
  795.   }
    
  796. }
    
  797. 
    
  798. export function markHiddenUpdate(
    
  799.   root: FiberRoot,
    
  800.   update: ConcurrentUpdate,
    
  801.   lane: Lane,
    
  802. ) {
    
  803.   const index = laneToIndex(lane);
    
  804.   const hiddenUpdates = root.hiddenUpdates;
    
  805.   const hiddenUpdatesForLane = hiddenUpdates[index];
    
  806.   if (hiddenUpdatesForLane === null) {
    
  807.     hiddenUpdates[index] = [update];
    
  808.   } else {
    
  809.     hiddenUpdatesForLane.push(update);
    
  810.   }
    
  811.   update.lane = lane | OffscreenLane;
    
  812. }
    
  813. 
    
  814. export function getBumpedLaneForHydration(
    
  815.   root: FiberRoot,
    
  816.   renderLanes: Lanes,
    
  817. ): Lane {
    
  818.   const renderLane = getHighestPriorityLane(renderLanes);
    
  819. 
    
  820.   let lane;
    
  821.   if (enableUnifiedSyncLane && (renderLane & SyncUpdateLanes) !== NoLane) {
    
  822.     lane = SyncHydrationLane;
    
  823.   } else {
    
  824.     switch (renderLane) {
    
  825.       case SyncLane:
    
  826.         lane = SyncHydrationLane;
    
  827.         break;
    
  828.       case InputContinuousLane:
    
  829.         lane = InputContinuousHydrationLane;
    
  830.         break;
    
  831.       case DefaultLane:
    
  832.         lane = DefaultHydrationLane;
    
  833.         break;
    
  834.       case TransitionLane1:
    
  835.       case TransitionLane2:
    
  836.       case TransitionLane3:
    
  837.       case TransitionLane4:
    
  838.       case TransitionLane5:
    
  839.       case TransitionLane6:
    
  840.       case TransitionLane7:
    
  841.       case TransitionLane8:
    
  842.       case TransitionLane9:
    
  843.       case TransitionLane10:
    
  844.       case TransitionLane11:
    
  845.       case TransitionLane12:
    
  846.       case TransitionLane13:
    
  847.       case TransitionLane14:
    
  848.       case TransitionLane15:
    
  849.       case RetryLane1:
    
  850.       case RetryLane2:
    
  851.       case RetryLane3:
    
  852.       case RetryLane4:
    
  853.         lane = TransitionHydrationLane;
    
  854.         break;
    
  855.       case IdleLane:
    
  856.         lane = IdleHydrationLane;
    
  857.         break;
    
  858.       default:
    
  859.         // Everything else is already either a hydration lane, or shouldn't
    
  860.         // be retried at a hydration lane.
    
  861.         lane = NoLane;
    
  862.         break;
    
  863.     }
    
  864.   }
    
  865. 
    
  866.   // Check if the lane we chose is suspended. If so, that indicates that we
    
  867.   // already attempted and failed to hydrate at that level. Also check if we're
    
  868.   // already rendering that lane, which is rare but could happen.
    
  869.   if ((lane & (root.suspendedLanes | renderLanes)) !== NoLane) {
    
  870.     // Give up trying to hydrate and fall back to client render.
    
  871.     return NoLane;
    
  872.   }
    
  873. 
    
  874.   return lane;
    
  875. }
    
  876. 
    
  877. export function addFiberToLanesMap(
    
  878.   root: FiberRoot,
    
  879.   fiber: Fiber,
    
  880.   lanes: Lanes | Lane,
    
  881. ) {
    
  882.   if (!enableUpdaterTracking) {
    
  883.     return;
    
  884.   }
    
  885.   if (!isDevToolsPresent) {
    
  886.     return;
    
  887.   }
    
  888.   const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
    
  889.   while (lanes > 0) {
    
  890.     const index = laneToIndex(lanes);
    
  891.     const lane = 1 << index;
    
  892. 
    
  893.     const updaters = pendingUpdatersLaneMap[index];
    
  894.     updaters.add(fiber);
    
  895. 
    
  896.     lanes &= ~lane;
    
  897.   }
    
  898. }
    
  899. 
    
  900. export function movePendingFibersToMemoized(root: FiberRoot, lanes: Lanes) {
    
  901.   if (!enableUpdaterTracking) {
    
  902.     return;
    
  903.   }
    
  904.   if (!isDevToolsPresent) {
    
  905.     return;
    
  906.   }
    
  907.   const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
    
  908.   const memoizedUpdaters = root.memoizedUpdaters;
    
  909.   while (lanes > 0) {
    
  910.     const index = laneToIndex(lanes);
    
  911.     const lane = 1 << index;
    
  912. 
    
  913.     const updaters = pendingUpdatersLaneMap[index];
    
  914.     if (updaters.size > 0) {
    
  915.       updaters.forEach(fiber => {
    
  916.         const alternate = fiber.alternate;
    
  917.         if (alternate === null || !memoizedUpdaters.has(alternate)) {
    
  918.           memoizedUpdaters.add(fiber);
    
  919.         }
    
  920.       });
    
  921.       updaters.clear();
    
  922.     }
    
  923. 
    
  924.     lanes &= ~lane;
    
  925.   }
    
  926. }
    
  927. 
    
  928. export function addTransitionToLanesMap(
    
  929.   root: FiberRoot,
    
  930.   transition: Transition,
    
  931.   lane: Lane,
    
  932. ) {
    
  933.   if (enableTransitionTracing) {
    
  934.     const transitionLanesMap = root.transitionLanes;
    
  935.     const index = laneToIndex(lane);
    
  936.     let transitions = transitionLanesMap[index];
    
  937.     if (transitions === null) {
    
  938.       transitions = new Set();
    
  939.     }
    
  940.     transitions.add(transition);
    
  941. 
    
  942.     transitionLanesMap[index] = transitions;
    
  943.   }
    
  944. }
    
  945. 
    
  946. export function getTransitionsForLanes(
    
  947.   root: FiberRoot,
    
  948.   lanes: Lane | Lanes,
    
  949. ): Array<Transition> | null {
    
  950.   if (!enableTransitionTracing) {
    
  951.     return null;
    
  952.   }
    
  953. 
    
  954.   const transitionsForLanes = [];
    
  955.   while (lanes > 0) {
    
  956.     const index = laneToIndex(lanes);
    
  957.     const lane = 1 << index;
    
  958.     const transitions = root.transitionLanes[index];
    
  959.     if (transitions !== null) {
    
  960.       transitions.forEach(transition => {
    
  961.         transitionsForLanes.push(transition);
    
  962.       });
    
  963.     }
    
  964. 
    
  965.     lanes &= ~lane;
    
  966.   }
    
  967. 
    
  968.   if (transitionsForLanes.length === 0) {
    
  969.     return null;
    
  970.   }
    
  971. 
    
  972.   return transitionsForLanes;
    
  973. }
    
  974. 
    
  975. export function clearTransitionsForLanes(root: FiberRoot, lanes: Lane | Lanes) {
    
  976.   if (!enableTransitionTracing) {
    
  977.     return;
    
  978.   }
    
  979. 
    
  980.   while (lanes > 0) {
    
  981.     const index = laneToIndex(lanes);
    
  982.     const lane = 1 << index;
    
  983. 
    
  984.     const transitions = root.transitionLanes[index];
    
  985.     if (transitions !== null) {
    
  986.       root.transitionLanes[index] = null;
    
  987.     }
    
  988. 
    
  989.     lanes &= ~lane;
    
  990.   }
    
  991. }