From df35e258b2ee3740c766907ad900de65ab4d0918 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Wed, 12 Jun 2024 11:31:56 -0700 Subject: [PATCH] Prevent Fusebox infra crash if RCTBridge is dealloc'ed off the main queue (#44877) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44877 Changelog: [Internal] We're seeing a sporadic iOS crash that suggests `[RCTBridge dealloc]` is being called off the main queue (despite a comment suggesting it shouldn't be). This exposes a race condition between destroying the `HostTarget` and attempting to unregister the instance+runtime from it . Here we use `RCTExecuteOnMainQueue` to make sure the `HostTarget` destruction is always sequenced after the `unregisterFromInspector()` call. Reviewed By: huntie Differential Revision: D58415684 fbshipit-source-id: a22e239c80c3204fe32b9e73719ffaa131feaffb --- packages/react-native/React/Base/RCTBridge.mm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/react-native/React/Base/RCTBridge.mm b/packages/react-native/React/Base/RCTBridge.mm index be1b40fe146c85..b97429823d63f0 100644 --- a/packages/react-native/React/Base/RCTBridge.mm +++ b/packages/react-native/React/Base/RCTBridge.mm @@ -313,10 +313,20 @@ - (void)dealloc // NOTE: RCTCxxBridge will use _inspectorTarget during [self invalidate], so we must // keep it alive until after the call returns. [self invalidate]; + + // `invalidate` is asynchronous if we aren't on the main queue. Unregister + // the HostTarget on the main queue so that `invalidate` can complete safely + // in that case. if (_inspectorPageId.has_value()) { - facebook::react::jsinspector_modern::getInspectorInstance().removePage(*_inspectorPageId); - _inspectorPageId.reset(); - _inspectorTarget.reset(); + // Since we can't keep using `self` after dealloc, steal its inspector + // state into block-mutable variables + __block auto inspectorPageId = std::move(_inspectorPageId); + __block auto inspectorTarget = std::move(_inspectorTarget); + RCTExecuteOnMainQueue(^{ + facebook::react::jsinspector_modern::getInspectorInstance().removePage(*inspectorPageId); + inspectorPageId.reset(); + inspectorTarget.reset(); + }); } }