From 24721ffcba267954f7e4f224b2939dec7f4258bf Mon Sep 17 00:00:00 2001 From: gnoff Date: Wed, 18 Oct 2023 17:11:34 +0000 Subject: [PATCH] [Fizz][Float] Do not write after closing the stream (#27541) Float methods can hang on to a reference to a Request after the request is closed due to AsyncLocalStorage. If a Float method is called at this point we do not want to attempt to flush anything. This change updates the closing logic to also call `stopFlowing` which will ensure that any checks against the destination properly reflect that we cannot do any writes. In addition it updates the enqueueFlush logic to existence check the destination inside the work function since it can change across the work scheduling gap if it is async. fixes: https://github.com/facebook/react/issues/27540 DiffTrain build for [601e5c38505ebc0ee099d8666b2f7a8b03159ac4](https://github.com/facebook/react/commit/601e5c38505ebc0ee099d8666b2f7a8b03159ac4) --- compiled/facebook-www/REVISION | 2 +- compiled/facebook-www/ReactART-dev.modern.js | 2 +- compiled/facebook-www/ReactART-prod.modern.js | 4 +- .../ReactDOMServer-dev.classic.js | 21 ++++++-- .../facebook-www/ReactDOMServer-dev.modern.js | 21 ++++++-- .../ReactDOMServer-prod.classic.js | 53 ++++++++++--------- .../ReactDOMServer-prod.modern.js | 53 ++++++++++--------- .../ReactDOMServerStreaming-dev.modern.js | 8 ++- .../ReactDOMServerStreaming-prod.modern.js | 9 ++-- 9 files changed, 106 insertions(+), 67 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index f172e74bf0f3d..98de1583a5d04 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -75c1bd7ee7e4a87a4dd0f560e157c57957ef823b +601e5c38505ebc0ee099d8666b2f7a8b03159ac4 diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index 08ca4ebb1deb5..74ffead6256af 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-modern-f972e958"; +var ReactVersion = "18.3.0-www-modern-6d0bc8c7"; var LegacyRoot = 0; var ConcurrentRoot = 1; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index b3c84ec0fc942..cc9edbc041899 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -9848,7 +9848,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-modern-4a0e1ba5", + version: "18.3.0-www-modern-75350665", rendererPackageName: "react-art" }; var internals$jscomp$inline_1302 = { @@ -9879,7 +9879,7 @@ var internals$jscomp$inline_1302 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-4a0e1ba5" + reconcilerVersion: "18.3.0-www-modern-75350665" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1303 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index 79dcd64f52159..ad9d0dcbffb90 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-classic-c2fb22b9"; +var ReactVersion = "18.3.0-www-classic-89c1183f"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -13209,7 +13209,10 @@ function flushCompletedQueues(request, destination) { } } // We're done. - close(destination); + close(destination); // We need to stop flowing now because we do not want any async contexts which might call + // float methods to initiate any flushes after this point + + stopFlowing(request); } } } @@ -13231,10 +13234,17 @@ function enqueueFlush(request) { // happen when we start flowing again request.destination !== null ) { - var destination = request.destination; request.flushScheduled = true; scheduleWork(function () { - return flushCompletedQueues(request, destination); + // We need to existence check destination again here because it might go away + // in between the enqueueFlush call and the work execution + var destination = request.destination; + + if (destination) { + flushCompletedQueues(request, destination); + } else { + request.flushScheduled = false; + } }); } } @@ -13264,6 +13274,9 @@ function startFlowing(request, destination) { fatalError(request, error); } } +function stopFlowing(request) { + request.destination = null; +} // This is called to early terminate a request. It puts all pending boundaries in client rendered state. function abort(request, reason) { try { diff --git a/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index ef65b16829158..6b9a8d9e1fb64 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-modern-f972e958"; +var ReactVersion = "18.3.0-www-modern-6d0bc8c7"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -12957,7 +12957,10 @@ function flushCompletedQueues(request, destination) { } } // We're done. - close(destination); + close(destination); // We need to stop flowing now because we do not want any async contexts which might call + // float methods to initiate any flushes after this point + + stopFlowing(request); } } } @@ -12979,10 +12982,17 @@ function enqueueFlush(request) { // happen when we start flowing again request.destination !== null ) { - var destination = request.destination; request.flushScheduled = true; scheduleWork(function () { - return flushCompletedQueues(request, destination); + // We need to existence check destination again here because it might go away + // in between the enqueueFlush call and the work execution + var destination = request.destination; + + if (destination) { + flushCompletedQueues(request, destination); + } else { + request.flushScheduled = false; + } }); } } @@ -13012,6 +13022,9 @@ function startFlowing(request, destination) { fatalError(request, error); } } +function stopFlowing(request) { + request.destination = null; +} // This is called to early terminate a request. It puts all pending boundaries in client rendered state. function abort(request, reason) { try { diff --git a/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index 31b08f1e36161..45819bf97d3aa 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -2287,16 +2287,16 @@ function hoistStylesheetDependency(stylesheet) { function createRenderState(resumableState, generateStaticMarkup) { var idPrefix = resumableState.idPrefix; resumableState = idPrefix + "P:"; - var JSCompiler_object_inline_segmentPrefix_1570 = idPrefix + "S:"; + var JSCompiler_object_inline_segmentPrefix_1574 = idPrefix + "S:"; idPrefix += "B:"; - var JSCompiler_object_inline_preconnects_1582 = new Set(), - JSCompiler_object_inline_fontPreloads_1583 = new Set(), - JSCompiler_object_inline_highImagePreloads_1584 = new Set(), - JSCompiler_object_inline_styles_1585 = new Map(), - JSCompiler_object_inline_bootstrapScripts_1586 = new Set(), - JSCompiler_object_inline_scripts_1587 = new Set(), - JSCompiler_object_inline_bulkPreloads_1588 = new Set(), - JSCompiler_object_inline_preloads_1589 = { + var JSCompiler_object_inline_preconnects_1586 = new Set(), + JSCompiler_object_inline_fontPreloads_1587 = new Set(), + JSCompiler_object_inline_highImagePreloads_1588 = new Set(), + JSCompiler_object_inline_styles_1589 = new Map(), + JSCompiler_object_inline_bootstrapScripts_1590 = new Set(), + JSCompiler_object_inline_scripts_1591 = new Set(), + JSCompiler_object_inline_bulkPreloads_1592 = new Set(), + JSCompiler_object_inline_preloads_1593 = { images: new Map(), stylesheets: new Map(), scripts: new Map(), @@ -2304,7 +2304,7 @@ function createRenderState(resumableState, generateStaticMarkup) { }; return { placeholderPrefix: resumableState, - segmentPrefix: JSCompiler_object_inline_segmentPrefix_1570, + segmentPrefix: JSCompiler_object_inline_segmentPrefix_1574, boundaryPrefix: idPrefix, startInlineScript: "