78
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
79
80
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
81
-const SyncUpdateRangeEnd = 1;
81
export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010;
83
-const SyncBatchedUpdateRangeEnd = 2;
82
83
export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100;
86
-const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011100;
87
-const InputDiscreteUpdateRangeStart = 3;
88
-const InputDiscreteUpdateRangeEnd = 5;
84
+const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011000;
85
86
const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000100000;
91
-const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011100000;
92
-const InputContinuousUpdateRangeStart = 6;
93
-const InputContinuousUpdateRangeEnd = 8;
87
+const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011000000;
88
89
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000100000000;
96
-export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111100000000;
97
-const DefaultUpdateRangeStart = 9;
98
-const DefaultUpdateRangeEnd = 12;
90
+export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111000000000;
91
92
const TransitionShortHydrationLane: Lane = /* */ 0b0000000000000000001000000000000;
101
-const TransitionShortLanes: Lanes = /* */ 0b0000000000000011111000000000000;
102
-const TransitionShortUpdateRangeStart = 13;
103
-const TransitionShortUpdateRangeEnd = 17;
93
+const TransitionShortLanes: Lanes = /* */ 0b0000000000000011110000000000000;
94
95
const TransitionLongHydrationLane: Lane = /* */ 0b0000000000000100000000000000000;
106
-const TransitionLongLanes: Lanes = /* */ 0b0000000001111100000000000000000;
107
-const TransitionLongUpdateRangeStart = 18;
108
-const TransitionLongUpdateRangeEnd = 22;
109
-
110
-// Includes all updates. Except Idle updates, which have special semantics.
111
-const UpdateRangeEnd = TransitionLongUpdateRangeEnd;
96
+const TransitionLongLanes: Lanes = /* */ 0b0000000001111000000000000000000;
97
98
const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000;
114
-const RetryRangeStart = 22;
115
-const RetryRangeEnd = 26;
99
100
export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000;
118
-const SelectiveHydrationRangeEnd = 27;
101
102
const NonIdleLanes = /* */ 0b0000111111111111111111111111111;
103
104
export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000;
123
-const IdleLanes: Lanes = /* */ 0b0111000000000000000000000000000;
124
-const IdleUpdateRangeStart = 28;
125
-const IdleUpdateRangeEnd = 30;
105
+const IdleLanes: Lanes = /* */ 0b0110000000000000000000000000000;
106
107
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
108
121
// "Registers" used to "return" multiple values
122
// Used by getHighestPriorityLanes and getNextLanes:
123
let return_highestLanePriority: LanePriority = DefaultLanePriority;
144
-let return_updateRangeEnd: number = -1;
124
125
function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
126
if ((SyncLane & lanes) !== NoLanes) {
127
return_highestLanePriority = SyncLanePriority;
149
- return_updateRangeEnd = SyncUpdateRangeEnd;
128
return SyncLane;
129
}
130
if ((SyncBatchedLane & lanes) !== NoLanes) {
131
return_highestLanePriority = SyncBatchedLanePriority;
154
- return_updateRangeEnd = SyncBatchedUpdateRangeEnd;
132
return SyncBatchedLane;
133
}
134
+ if ((InputDiscreteHydrationLane & lanes) !== NoLanes) {
135
+ return_highestLanePriority = InputDiscreteHydrationLanePriority;
136
+ return InputDiscreteHydrationLane;
137
+ }
138
const inputDiscreteLanes = InputDiscreteLanes & lanes;
139
if (inputDiscreteLanes !== NoLanes) {
159
- if (inputDiscreteLanes & InputDiscreteHydrationLane) {
160
- return_highestLanePriority = InputDiscreteHydrationLanePriority;
161
- return_updateRangeEnd = InputDiscreteUpdateRangeStart;
162
- return InputDiscreteHydrationLane;
163
- } else {
164
- return_highestLanePriority = InputDiscreteLanePriority;
165
- return_updateRangeEnd = InputDiscreteUpdateRangeEnd;
166
- return inputDiscreteLanes;
167
- }
140
+ return_highestLanePriority = InputDiscreteLanePriority;
141
+ return inputDiscreteLanes;
142
+ }
143
+ if ((lanes & InputContinuousHydrationLane) !== NoLanes) {
144
+ return_highestLanePriority = InputContinuousHydrationLanePriority;
145
+ return InputContinuousHydrationLane;
146
}
147
const inputContinuousLanes = InputContinuousLanes & lanes;
148
if (inputContinuousLanes !== NoLanes) {
171
- if (inputContinuousLanes & InputContinuousHydrationLane) {
172
- return_highestLanePriority = InputContinuousHydrationLanePriority;
173
- return_updateRangeEnd = InputContinuousUpdateRangeStart;
174
- return InputContinuousHydrationLane;
175
- } else {
176
- return_highestLanePriority = InputContinuousLanePriority;
177
- return_updateRangeEnd = InputContinuousUpdateRangeEnd;
178
- return inputContinuousLanes;
179
- }
149
+ return_highestLanePriority = InputContinuousLanePriority;
150
+ return inputContinuousLanes;
151
+ }
152
+ if ((lanes & DefaultHydrationLane) !== NoLanes) {
153
+ return_highestLanePriority = DefaultHydrationLanePriority;
154
+ return DefaultHydrationLane;
155
}
156
const defaultLanes = DefaultLanes & lanes;
157
if (defaultLanes !== NoLanes) {
183
- if (defaultLanes & DefaultHydrationLane) {
184
- return_highestLanePriority = DefaultHydrationLanePriority;
185
- return_updateRangeEnd = DefaultUpdateRangeStart;
186
- return DefaultHydrationLane;
187
- } else {
188
- return_highestLanePriority = DefaultLanePriority;
189
- return_updateRangeEnd = DefaultUpdateRangeEnd;
190
- return defaultLanes;
191
- }
158
+ return_highestLanePriority = DefaultLanePriority;
159
+ return defaultLanes;
160
+ }
161
+ if ((lanes & TransitionShortHydrationLane) !== NoLanes) {
162
+ return_highestLanePriority = TransitionShortHydrationLanePriority;
163
+ return TransitionShortHydrationLane;
164
}
165
const transitionShortLanes = TransitionShortLanes & lanes;
166
if (transitionShortLanes !== NoLanes) {
195
- if (transitionShortLanes & TransitionShortHydrationLane) {
196
- return_highestLanePriority = TransitionShortHydrationLanePriority;
197
- return_updateRangeEnd = TransitionShortUpdateRangeStart;
198
- return TransitionShortHydrationLane;
199
- } else {
200
- return_highestLanePriority = TransitionShortLanePriority;
201
- return_updateRangeEnd = TransitionShortUpdateRangeEnd;
202
- return transitionShortLanes;
203
- }
167
+ return_highestLanePriority = TransitionShortLanePriority;
168
+ return transitionShortLanes;
169
+ }
170
+ if ((lanes & TransitionLongHydrationLane) !== NoLanes) {
171
+ return_highestLanePriority = TransitionLongHydrationLanePriority;
172
+ return TransitionLongHydrationLane;
173
}
174
const transitionLongLanes = TransitionLongLanes & lanes;
175
if (transitionLongLanes !== NoLanes) {
207
- if (transitionLongLanes & TransitionLongHydrationLane) {
208
- return_highestLanePriority = TransitionLongHydrationLanePriority;
209
- return_updateRangeEnd = TransitionLongUpdateRangeStart;
210
- return TransitionLongHydrationLane;
211
- } else {
212
- return_highestLanePriority = TransitionLongLanePriority;
213
- return_updateRangeEnd = TransitionLongUpdateRangeEnd;
214
- return transitionLongLanes;
215
- }
176
+ return_highestLanePriority = TransitionLongLanePriority;
177
+ return transitionLongLanes;
178
}
179
const retryLanes = RetryLanes & lanes;
180
if (retryLanes !== NoLanes) {
181
return_highestLanePriority = RetryLanePriority;
220
- return_updateRangeEnd = RetryRangeEnd;
182
return retryLanes;
183
}
184
if (lanes & SelectiveHydrationLane) {
185
return_highestLanePriority = SelectiveHydrationLanePriority;
225
- return_updateRangeEnd = SelectiveHydrationRangeEnd;
186
return SelectiveHydrationLane;
187
}
188
+ if ((lanes & IdleHydrationLane) !== NoLanes) {
189
+ return_highestLanePriority = IdleHydrationLanePriority;
190
+ return IdleHydrationLane;
191
+ }
192
const idleLanes = IdleLanes & lanes;
193
if (idleLanes !== NoLanes) {
230
- if (idleLanes & IdleHydrationLane) {
231
- return_highestLanePriority = IdleHydrationLanePriority;
232
- return_updateRangeEnd = IdleUpdateRangeStart;
233
- return IdleHydrationLane;
234
- } else {
235
- return_highestLanePriority = IdleLanePriority;
236
- return_updateRangeEnd = IdleUpdateRangeEnd;
237
- return idleLanes;
238
- }
194
+ return_highestLanePriority = IdleLanePriority;
195
+ return idleLanes;
196
}
197
if ((OffscreenLane & lanes) !== NoLanes) {
198
return_highestLanePriority = OffscreenLanePriority;
242
- return_updateRangeEnd = TotalLanes;
199
return OffscreenLane;
200
}
201
if (__DEV__) {
203
}
204
// This shouldn't be reachable, but as a fallback, return the entire bitmask.
205
return_highestLanePriority = DefaultLanePriority;
250
- return_updateRangeEnd = DefaultUpdateRangeEnd;
206
return lanes;
207
}
208
271
272
let nextLanes = NoLanes;
273
let nextLanePriority = NoLanePriority;
319
- let equalOrHigherPriorityLanes = NoLanes;
274
275
const expiredLanes = root.expiredLanes;
276
const suspendedLanes = root.suspendedLanes;
280
if (expiredLanes !== NoLanes) {
281
nextLanes = expiredLanes;
282
nextLanePriority = return_highestLanePriority = SyncLanePriority;
329
- equalOrHigherPriorityLanes = (getLowestPriorityLane(nextLanes) << 1) - 1;
283
} else {
284
// Do not work on any idle work until all the non-idle work has finished,
285
// even if the work is suspended.
289
if (nonIdleUnblockedLanes !== NoLanes) {
290
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
291
nextLanePriority = return_highestLanePriority;
339
- equalOrHigherPriorityLanes = (1 << return_updateRangeEnd) - 1;
292
} else {
293
const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
294
if (nonIdlePingedLanes !== NoLanes) {
295
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
296
nextLanePriority = return_highestLanePriority;
345
- equalOrHigherPriorityLanes = (1 << return_updateRangeEnd) - 1;
297
}
298
}
299
} else {
302
if (unblockedLanes !== NoLanes) {
303
nextLanes = getHighestPriorityLanes(unblockedLanes);
304
nextLanePriority = return_highestLanePriority;
354
- equalOrHigherPriorityLanes = (1 << return_updateRangeEnd) - 1;
305
} else {
306
if (pingedLanes !== NoLanes) {
307
nextLanes = getHighestPriorityLanes(pingedLanes);
308
nextLanePriority = return_highestLanePriority;
359
- equalOrHigherPriorityLanes = (1 << return_updateRangeEnd) - 1;
309
}
310
}
311
}
319
320
// If there are higher priority lanes, we'll include them even if they
321
// are suspended.
373
- nextLanes = pendingLanes & equalOrHigherPriorityLanes;
322
+ nextLanes = pendingLanes & getEqualOrHigherPriorityLanes(nextLanes);
323
324
// If we're already in the middle of a render, switching lanes will interrupt
325
// it and we'll lose our progress. We should only do this if the new lanes are
469
case SyncBatchedLanePriority:
470
return SyncBatchedLane;
471
case InputDiscreteLanePriority: {
523
- let lane = findLane(
524
- InputDiscreteUpdateRangeStart,
525
- UpdateRangeEnd,
526
- wipLanes,
527
- );
472
+ const lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);
473
if (lane === NoLane) {
529
- lane = InputDiscreteHydrationLane;
474
+ // Shift to the next priority level
475
+ return findUpdateLane(InputContinuousLanePriority, wipLanes);
476
}
477
return lane;
478
}
479
case InputContinuousLanePriority: {
534
- let lane = findLane(
535
- InputContinuousUpdateRangeStart,
536
- UpdateRangeEnd,
537
- wipLanes,
538
- );
480
+ const lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);
481
if (lane === NoLane) {
540
- lane = InputContinuousHydrationLane;
482
+ // Shift to the next priority level
483
+ return findUpdateLane(DefaultLanePriority, wipLanes);
484
}
485
return lane;
486
}
487
case DefaultLanePriority: {
545
- let lane = findLane(DefaultUpdateRangeStart, UpdateRangeEnd, wipLanes);
488
+ let lane = pickArbitraryLane(DefaultLanes & ~wipLanes);
489
if (lane === NoLane) {
547
- lane = DefaultHydrationLane;
490
+ // If all the default lanes are already being worked on, look for a
491
+ // lane in the transition range.
492
+ lane = pickArbitraryLane(
493
+ (TransitionShortLanes | TransitionLongLanes) & ~wipLanes,
494
+ );
495
+ if (lane === NoLane) {
496
+ // All the transition lanes are taken, too. This should be very
497
+ // rare, but as a last resort, pick a default lane. This will have
498
+ // the effect of interrupting the current work-in-progress render.
499
+ lane = pickArbitraryLane(DefaultLanes);
500
+ }
501
}
502
return lane;
503
}
551
- case TransitionShortLanePriority:
504
+ case TransitionShortLanePriority: // Should be handled by findTransitionLane instead
505
case TransitionLongLanePriority:
553
- // Should be handled by findTransitionLane instead
554
- break;
555
- case RetryLanePriority:
556
- // Should be handled by findRetryLane instead
506
+ case RetryLanePriority: // Should be handled by findRetryLane instead
507
break;
508
case IdleLanePriority:
559
- let lane = findLane(IdleUpdateRangeStart, IdleUpdateRangeEnd, wipLanes);
509
+ let lane = pickArbitraryLane(IdleLanes & ~wipLanes);
510
if (lane === NoLane) {
561
- lane = IdleHydrationLane;
511
+ lane = pickArbitraryLane(IdleLanes);
512
}
513
return lane;
514
default:
530
pendingLanes: Lanes,
531
): Lane {
532
if (lanePriority === TransitionShortLanePriority) {
583
- let lane = findLane(
584
- TransitionShortUpdateRangeStart,
585
- TransitionShortUpdateRangeEnd,
586
- wipLanes | pendingLanes,
587
- );
533
+ // First look for lanes that are completely unclaimed, i.e. have no
534
+ // pending work.
535
+ let lane = pickArbitraryLane(TransitionShortLanes & ~pendingLanes);
536
if (lane === NoLane) {
589
- lane = findLane(
590
- TransitionShortUpdateRangeStart,
591
- TransitionShortUpdateRangeEnd,
592
- wipLanes,
593
- );
537
+ // If all lanes have pending work, look for a lane that isn't currently
538
+ // being worked on.
539
+ lane = pickArbitraryLane(TransitionShortLanes & ~wipLanes);
540
if (lane === NoLane) {
595
- lane = TransitionShortHydrationLane;
541
+ // If everything is being worked on, pick any lane. This has the
542
+ // effect of interrupting the current work-in-progress.
543
+ lane = pickArbitraryLane(TransitionShortLanes);
544
}
545
}
546
return lane;
547
}
548
if (lanePriority === TransitionLongLanePriority) {
601
- let lane = findLane(
602
- TransitionLongUpdateRangeStart,
603
- TransitionLongUpdateRangeEnd,
604
- wipLanes | pendingLanes,
605
- );
549
+ // First look for lanes that are completely unclaimed, i.e. have no
550
+ // pending work.
551
+ let lane = pickArbitraryLane(TransitionLongLanes & ~pendingLanes);
552
if (lane === NoLane) {
607
- lane = findLane(
608
- TransitionLongUpdateRangeStart,
609
- TransitionLongUpdateRangeEnd,
610
- wipLanes,
611
- );
553
+ // If all lanes have pending work, look for a lane that isn't currently
554
+ // being worked on.
555
+ lane = pickArbitraryLane(TransitionLongLanes & ~wipLanes);
556
if (lane === NoLane) {
613
- lane = TransitionLongHydrationLane;
557
+ // If everything is being worked on, pick any lane. This has the
558
+ // effect of interrupting the current work-in-progress.
559
+ lane = pickArbitraryLane(TransitionLongLanes);
560
}
561
}
562
return lane;
574
// This is a fork of `findUpdateLane` designed specifically for Suspense
575
// "retries" — a special update that attempts to flip a Suspense boundary
576
// from its placeholder state to its primary/resolved state.
631
- let lane = findLane(RetryRangeStart, RetryRangeEnd, wipLanes);
577
+ let lane = pickArbitraryLane(RetryLanes & ~wipLanes);
578
if (lane === NoLane) {
579
lane = pickArbitraryLane(RetryLanes);
580
}
581
return lane;
582
}
583
638
-function findLane(start, end, skipLanes) {
639
- // This finds the first bit between the `start` and `end` positions that isn't
640
- // in `skipLanes`.
641
- // TODO: This will always favor the rightmost bits. That's usually fine
642
- // because any bit that's pending will be part of `skipLanes`, so we'll do our
643
- // best to avoid accidental entanglement. However, lanes that are pending
644
- // inside an Offscreen tree aren't considered "pending" at the root level. So
645
- // they aren't included in `skipLanes`. So we should try not to favor any
646
- // particular part of the range, perhaps by incrementing an offset for each
647
- // distinct event. Must be the same within a single event, though.
648
- const bitsInRange = ((1 << (end - start)) - 1) << start;
649
- const possibleBits = bitsInRange & ~skipLanes;
650
- const leastSignificantBit = possibleBits & -possibleBits;
651
- return leastSignificantBit;
584
+function getHighestPriorityLane(lanes: Lanes) {
585
+ return lanes & -lanes;
586
}
587
588
function getLowestPriorityLane(lanes: Lanes): Lane {
591
return index < 0 ? NoLanes : 1 << index;
592
}
593
594
+function getEqualOrHigherPriorityLanes(lanes: Lanes | Lane): Lanes {
595
+ return (getLowestPriorityLane(lanes) << 1) - 1;
596
+}
597
+
598
export function pickArbitraryLane(lanes: Lanes): Lane {
661
- return getLowestPriorityLane(lanes);
599
+ // This wrapper function gets inlined. Only exists so to communicate that it
600
+ // doesn't matter which bit is selected; you can pick any bit without
601
+ // affecting the algorithms where its used. Here I'm using
602
+ // getHighestPriorityLane because it requires the fewest operations.
603
+ return getHighestPriorityLane(lanes);
604
}
605
606
function pickArbitraryLaneIndex(lanes: Lane | Lanes) {