From a949e0d751e4a36dec7459f14f89d7a87bb73b69 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Tue, 20 Aug 2024 17:50:55 -0700 Subject: [PATCH] Report early js exceptions on the js thread (#46116) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46116 If your app raises an early js exception, and you cold start it, often you'll see this error: ``` SurfaceRegistryBinding::startSurface failed. Global was not installed. ``` {F1807125099} The reason why is because two different threads race to redbox: * The nativemodule thread: the early js error (reported [here](https://fburl.com/code/vcrqzsdp)) * The javascript thread: the SurfaceRegistryBinding error (a subsequent native -> js call) After this diff, the early js error will **not jump onto the nativemodule thread** to report this error. This ensures that we "always" (to the best of my knowledge) see the early js error first. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D61339213 fbshipit-source-id: f1b9ab30150b87377817c2fd93ca349c406db48b --- .../facebook/react/runtime/ReactInstance.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java index 81e81c85022..26fb9b93dbe 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java @@ -156,7 +156,7 @@ final class ReactInstance { nativeModulesMessageQueueThread, mJavaTimerManager, jsTimerExecutor, - new ReactJsExceptionHandlerImpl(nativeModulesMessageQueueThread), + new ReactJsExceptionHandlerImpl(exceptionHandler), bindingsInstaller, isProfiling, reactHostInspectorTarget); @@ -318,25 +318,28 @@ final class ReactInstance { } private class ReactJsExceptionHandlerImpl implements ReactJsExceptionHandler { - private final MessageQueueThread mMessageQueueThread; + private final QueueThreadExceptionHandler mQueueThreadExceptionHandler; - ReactJsExceptionHandlerImpl(MessageQueueThread nativeModulesMessageQueueThread) { - mMessageQueueThread = nativeModulesMessageQueueThread; + ReactJsExceptionHandlerImpl(QueueThreadExceptionHandler queueThreadExceptionHandler) { + mQueueThreadExceptionHandler = queueThreadExceptionHandler; } @Override public void reportJsException(ParsedError error) { JavaOnlyMap data = StackTraceHelper.convertParsedError(error); - // Simulate async native module method call - mMessageQueueThread.runOnQueue( - () -> { - NativeExceptionsManagerSpec exceptionsManager = - (NativeExceptionsManagerSpec) - Assertions.assertNotNull( - mTurboModuleManager.getModule(NativeExceptionsManagerSpec.NAME)); - exceptionsManager.reportException(data); - }); + try { + NativeExceptionsManagerSpec exceptionsManager = + (NativeExceptionsManagerSpec) + Assertions.assertNotNull( + mTurboModuleManager.getModule(NativeExceptionsManagerSpec.NAME)); + exceptionsManager.reportException(data); + } catch (Exception e) { + // Sometimes (e.g: always with the default exception manager) the native module exceptions + // manager can throw. In those cases, call into the lower-level queue thread exceptions + // handler. + mQueueThreadExceptionHandler.handleException(e); + } } }