diff --git a/packages/react-native/ReactCommon/cxxreact/JSBigString.h b/packages/react-native/ReactCommon/cxxreact/JSBigString.h index 3668b3254625..a2365dee8962 100644 --- a/packages/react-native/ReactCommon/cxxreact/JSBigString.h +++ b/packages/react-native/ReactCommon/cxxreact/JSBigString.h @@ -140,6 +140,11 @@ class RN_EXPORT JSBigFileString : public JSBigString { size_t size() const override; int fd() const; + // Throws std::runtime_error when the file at sourceURL cannot be opened or + // read (e.g. it is missing). Callers that load files which may legitimately + // be gone at load time (such as an OS-purgeable / LRU-evictable cache) must + // catch this and degrade gracefully rather than let it surface as an + // unhandled exception. static std::unique_ptr fromPath(const std::string &sourceURL); private: diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index e6b8f129d5f4..288bde9d70f6 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -378,26 +378,39 @@ void ReactInstance::registerSegment( const std::string& segmentPath) { LOG(WARNING) << "Starting to run ReactInstance::registerSegment with segment " << segmentId; - runtimeScheduler_->scheduleWork([=](jsi::Runtime& runtime) { - TraceSection s("ReactInstance::registerSegment"); - auto tag = std::to_string(segmentId); - auto script = JSBigFileString::fromPath(segmentPath); - if (script->size() == 0) { - throw std::invalid_argument( - "Empty segment registered with ID " + tag + " from " + segmentPath); - } - - ReactMarker::logTaggedMarker( - ReactMarker::REGISTER_JS_SEGMENT_START, tag.c_str()); - LOG(WARNING) << "Starting to evaluate segment " << segmentId - << " in ReactInstance::registerSegment"; - runtime.evaluateJavaScript( - std::move(script), getSyntheticBundlePath(segmentId)); - LOG(WARNING) << "Finished evaluating segment " << segmentId - << " in ReactInstance::registerSegment"; - ReactMarker::logTaggedMarker( - ReactMarker::REGISTER_JS_SEGMENT_STOP, tag.c_str()); - }); + // Build the segment buffer off the JS thread: there's no need to block the + // JS thread on file I/O. The segment lives in an OS-purgeable / + // LRU-evictable on-demand cache and can already be gone by the time we get + // here, in which case fromPath throws. Catch it and return early so a + // missing segment degrades gracefully instead of surfacing as an unhandled + // exception. + std::shared_ptr script; + try { + script = JSBigFileString::fromPath(segmentPath); + } catch (const std::exception& e) { + LOG(ERROR) << "ReactInstance::registerSegment - could not load segment " + << segmentId << " from " << segmentPath << ": " << e.what(); + return; + } + if (script->size() == 0) { + throw std::invalid_argument( + "Empty segment registered with ID " + std::to_string(segmentId) + + " from " + segmentPath); + } + runtimeScheduler_->scheduleWork( + [script = std::move(script), segmentId](jsi::Runtime& runtime) { + TraceSection s("ReactInstance::registerSegment"); + auto tag = std::to_string(segmentId); + ReactMarker::logTaggedMarker( + ReactMarker::REGISTER_JS_SEGMENT_START, tag.c_str()); + LOG(WARNING) << "Starting to evaluate segment " << segmentId + << " in ReactInstance::registerSegment"; + runtime.evaluateJavaScript(script, getSyntheticBundlePath(segmentId)); + LOG(WARNING) << "Finished evaluating segment " << segmentId + << " in ReactInstance::registerSegment"; + ReactMarker::logTaggedMarker( + ReactMarker::REGISTER_JS_SEGMENT_STOP, tag.c_str()); + }); } namespace {