Skip to content

Commit b25f3d4

Browse files
committed
Reimlements with a flushing state flag to avoid an increasing epoch counter
1 parent a855a5a commit b25f3d4

File tree

2 files changed

+37
-42
lines changed

2 files changed

+37
-42
lines changed

packages/react-server/src/ReactFizzServer.js

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,14 @@ const OPEN = 0;
296296
const CLOSING = 1;
297297
const CLOSED = 2;
298298

299+
type ScheduleState = 10 | 11 | 12;
300+
const IDLE = 10;
301+
const WORK = 11;
302+
const FLUSH = 12;
303+
299304
export opaque type Request = {
300305
destination: null | Destination,
301-
epoch: number,
302-
flushScheduled: boolean,
306+
schedule: ScheduleState,
303307
+resumableState: ResumableState,
304308
+renderState: RenderState,
305309
+rootFormatContext: FormatContext,
@@ -381,8 +385,7 @@ export function createRequest(
381385
const abortSet: Set<Task> = new Set();
382386
const request: Request = {
383387
destination: null,
384-
epoch: 0,
385-
flushScheduled: false,
388+
schedule: IDLE,
386389
resumableState,
387390
renderState,
388391
rootFormatContext,
@@ -494,8 +497,7 @@ export function resumeRequest(
494497
const abortSet: Set<Task> = new Set();
495498
const request: Request = {
496499
destination: null,
497-
epoch: 0,
498-
flushScheduled: false,
500+
schedule: IDLE,
499501
resumableState: postponedState.resumableState,
500502
renderState,
501503
rootFormatContext: postponedState.rootFormatContext,
@@ -4304,23 +4306,23 @@ function flushCompletedQueues(
43044306
}
43054307
}
43064308

4307-
function completeWorkEpoch(request: Request) {
4308-
request.epoch++;
4309+
function flushWork(request: Request) {
4310+
request.schedule = IDLE;
43094311
const destination = request.destination;
43104312
if (destination) {
43114313
flushCompletedQueues(request, destination);
43124314
}
43134315
}
43144316

43154317
function startPerformingWork(request: Request): void {
4316-
request.epoch++;
4318+
request.schedule = WORK;
43174319
if (supportsRequestStorage) {
43184320
scheduleWork(() => requestStorage.run(request, performWork, request));
43194321
} else {
43204322
scheduleWork(() => performWork(request));
43214323
}
43224324
scheduleWork(() => {
4323-
completeWorkEpoch(request);
4325+
flushWork(request);
43244326
});
43254327
}
43264328

@@ -4357,31 +4359,23 @@ function enqueueEarlyPreloadsAfterInitialWork(request: Request) {
43574359

43584360
function enqueueFlush(request: Request): void {
43594361
if (
4360-
request.flushScheduled === false &&
4362+
request.schedule === IDLE &&
43614363
// If there are pinged tasks we are going to flush anyway after work completes
43624364
request.pingedTasks.length === 0 &&
43634365
// If there is no destination there is nothing we can flush to. A flush will
43644366
// happen when we start flowing again
43654367
request.destination !== null
43664368
) {
4367-
request.flushScheduled = true;
4368-
const currentEpoch = request.epoch;
4369+
request.schedule = FLUSH;
43694370
scheduleWork(() => {
4370-
// In builds where scheduleWork is synchronous this will always initiate a
4371-
// flush immediately. That's not ideal but it's not what we're optimizing for
4372-
// and we ought to consider not using the sync form except for legacy. Regardless
4373-
// the logic is still sound because the epoch and destination could not have
4374-
// changed so while we're doing unecessary checks here it still preserves the same
4375-
// semantics as the async case.
4376-
4377-
request.flushScheduled = false;
4378-
if (currentEpoch !== request.epoch) {
4379-
// We scheduled this flush when no work was being performed but since
4380-
// then we've started a new epoch (we're either rendering or we've already flushed)
4381-
// so we don't need to flush here anymore.
4371+
if (request.schedule !== FLUSH) {
4372+
// We already flushed or we started a new render and will let that finish first
4373+
// which will end up flushing so we have nothing to do here.
43824374
return;
43834375
}
43844376

4377+
request.schedule = IDLE;
4378+
43854379
// We need to existence check destination again here because it might go away
43864380
// in between the enqueueFlush call and the work execution
43874381
const destination = request.destination;

packages/react-server/src/ReactFlightServer.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,14 @@ type Task = {
279279

280280
interface Reference {}
281281

282+
type ScheduleState = 10 | 11 | 12;
283+
const IDLE = 10;
284+
const WORK = 11;
285+
const FLUSH = 12;
286+
282287
export type Request = {
283288
status: 0 | 1 | 2,
284-
epoch: number,
285-
flushScheduled: boolean,
289+
schedule: ScheduleState,
286290
fatalError: mixed,
287291
destination: null | Destination,
288292
bundlerConfig: ClientManifest,
@@ -379,8 +383,7 @@ export function createRequest(
379383
const hints = createHints();
380384
const request: Request = ({
381385
status: OPEN,
382-
epoch: 0,
383-
flushScheduled: false,
386+
schedule: IDLE,
384387
fatalError: null,
385388
destination: null,
386389
bundlerConfig,
@@ -3104,42 +3107,40 @@ function flushCompletedChunks(
31043107
}
31053108
}
31063109

3107-
function completeWorkEpoch(request: Request) {
3108-
request.epoch++;
3110+
function flushWork(request: Request) {
3111+
request.schedule = IDLE;
31093112
const destination = request.destination;
31103113
if (destination) {
31113114
flushCompletedChunks(request, destination);
31123115
}
31133116
}
31143117

31153118
function startPerformingWork(request: Request): void {
3116-
request.epoch++;
3119+
request.schedule = WORK;
31173120
if (supportsRequestStorage) {
31183121
scheduleWork(() => requestStorage.run(request, performWork, request));
31193122
} else {
31203123
scheduleWork(() => performWork(request));
31213124
}
31223125
scheduleWork(() => {
3123-
completeWorkEpoch(request);
3126+
flushWork(request);
31243127
});
31253128
}
31263129

31273130
export function startWork(request: Request): void {
3128-
request.flushScheduled = request.destination !== null;
31293131
startPerformingWork(request);
31303132
}
31313133

31323134
function enqueueFlush(request: Request): void {
31333135
if (
3134-
request.flushScheduled === false &&
3136+
request.schedule === IDLE &&
31353137
// If there are pinged tasks we are going to flush anyway after work completes
31363138
request.pingedTasks.length === 0 &&
31373139
// If there is no destination there is nothing we can flush to. A flush will
31383140
// happen when we start flowing again
31393141
request.destination !== null
31403142
) {
3141-
request.flushScheduled = true;
3142-
const currentEpoch = request.epoch;
3143+
request.schedule = FLUSH;
31433144
scheduleWork(() => {
31443145
// In builds where scheduleWork is synchronous this will always initiate a
31453146
// flush immediately. That's not ideal but it's not what we're optimizing for
@@ -3148,14 +3149,14 @@ function enqueueFlush(request: Request): void {
31483149
// changed so while we're doing unecessary checks here it still preserves the same
31493150
// semantics as the async case.
31503151

3151-
request.flushScheduled = false;
3152-
if (currentEpoch !== request.epoch) {
3153-
// We scheduled this flush when no work was being performed but since
3154-
// then we've started a new epoch (we're either rendering or we've already flushed)
3155-
// so we don't need to flush here anymore.
3152+
if (request.schedule !== FLUSH) {
3153+
// We already flushed or we started a new render and will let that finish first
3154+
// which will end up flushing so we have nothing to do here.
31563155
return;
31573156
}
31583157

3158+
request.schedule = IDLE;
3159+
31593160
// We need to existence check destination again here because it might go away
31603161
// in between the enqueueFlush call and the work execution
31613162
const destination = request.destination;

0 commit comments

Comments
 (0)