diff --git a/change/1756980197-fix-deadlock.json b/change/1756980197-fix-deadlock.json new file mode 100644 index 00000000000..866ed7a7c20 --- /dev/null +++ b/change/1756980197-fix-deadlock.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Fix deadlock in ReactInstanceWin::DetachRootView by replacing sync call with async", + "packageName": "react-native-windows", + "email": "copilot@github.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Microsoft.ReactNative/QuirkSettings.cpp b/vnext/Microsoft.ReactNative/QuirkSettings.cpp index 2ceca4b8a8a..1c817aa0f0a 100644 --- a/vnext/Microsoft.ReactNative/QuirkSettings.cpp +++ b/vnext/Microsoft.ReactNative/QuirkSettings.cpp @@ -77,6 +77,12 @@ winrt::Microsoft::ReactNative::ReactPropertyId UseRuntimeSchedulerProperty return propId; } +winrt::Microsoft::ReactNative::ReactPropertyId UseRunOnQueueSyncProperty() noexcept { + winrt::Microsoft::ReactNative::ReactPropertyId propId{L"ReactNative.QuirkSettings", L"UseRunOnQueueSync"}; + + return propId; +} + #pragma region IDL interface /*static*/ void QuirkSettings::SetMatchAndroidAndIOSStretchBehavior( @@ -121,6 +127,12 @@ winrt::Microsoft::ReactNative::ReactPropertyId UseRuntimeSchedulerProperty ReactPropertyBag(settings.Properties()).Set(UseRuntimeSchedulerProperty(), value); } +/*static*/ void QuirkSettings::SetUseRunOnQueueSync( + winrt::Microsoft::ReactNative::ReactInstanceSettings settings, + bool value) noexcept { + ReactPropertyBag(settings.Properties()).Set(UseRunOnQueueSyncProperty(), value); +} + #pragma endregion IDL interface /*static*/ bool QuirkSettings::GetMatchAndroidAndIOSStretchBehavior(ReactPropertyBag properties) noexcept { @@ -153,4 +165,8 @@ winrt::Microsoft::ReactNative::ReactPropertyId UseRuntimeSchedulerProperty return properties.Get(UseRuntimeSchedulerProperty()).value_or(true); } +/*static*/ bool QuirkSettings::GetUseRunOnQueueSync(ReactPropertyBag properties) noexcept { + return properties.Get(UseRunOnQueueSyncProperty()).value_or(false); +} + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/QuirkSettings.h b/vnext/Microsoft.ReactNative/QuirkSettings.h index 507c755c0d2..f19691a20c6 100644 --- a/vnext/Microsoft.ReactNative/QuirkSettings.h +++ b/vnext/Microsoft.ReactNative/QuirkSettings.h @@ -30,6 +30,7 @@ struct QuirkSettings : QuirkSettingsT { winrt::Microsoft::ReactNative::ReactPropertyBag properties) noexcept; static bool GetSuppressWindowFocusOnViewFocus(winrt::Microsoft::ReactNative::ReactPropertyBag properties) noexcept; static bool GetUseRuntimeScheduler(winrt::Microsoft::ReactNative::ReactPropertyBag properties) noexcept; + static bool GetUseRunOnQueueSync(winrt::Microsoft::ReactNative::ReactPropertyBag properties) noexcept; static bool GetEnableFabric(winrt::Microsoft::ReactNative::ReactPropertyBag properties) noexcept; @@ -65,6 +66,8 @@ struct QuirkSettings : QuirkSettingsT { winrt::Microsoft::ReactNative::ReactInstanceSettings settings, bool value) noexcept; + static void SetUseRunOnQueueSync(winrt::Microsoft::ReactNative::ReactInstanceSettings settings, bool value) noexcept; + #pragma endregion Public API - part of IDL interface }; diff --git a/vnext/Microsoft.ReactNative/QuirkSettings.idl b/vnext/Microsoft.ReactNative/QuirkSettings.idl index a4ad798aced..0770d5faa3f 100644 --- a/vnext/Microsoft.ReactNative/QuirkSettings.idl +++ b/vnext/Microsoft.ReactNative/QuirkSettings.idl @@ -64,6 +64,11 @@ namespace Microsoft.ReactNative "By default `react-native-windows` will use the new RuntimeScheduler." "Setting this to false will revert the behavior to previous scheduling logic.") static void SetUseRuntimeScheduler(ReactInstanceSettings settings, Boolean value); + + DOC_STRING( + "By default `react-native-windows` will not call sync JS call: runOnQueueSync." + "Setting this to true will revert the behavior to previous logic and might result in hang.") + static void SetUseRunOnQueueSync(ReactInstanceSettings settings, Boolean value); } } // namespace Microsoft.ReactNative diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index aefaaa3db63..906364b8ab3 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -1514,8 +1514,15 @@ void ReactInstanceWin::DetachRootView(facebook::react::IReactRootView *rootView, CallJsFunction("AppRegistry", "unmountApplicationComponentAtRootTag", std::move(params)); } - // Give the JS thread time to finish executing - m_jsMessageThread.Load()->runOnQueueSync([]() {}); + // QUIRK: Legacy sync behavior can be re-enabled via EnableSyncDetachRootView option + // The sync call was removed to prevent deadlocks with Hermes GC operations + bool useSyncCall = winrt::Microsoft::ReactNative::implementation::QuirkSettings::GetUseRunOnQueueSync( + winrt::Microsoft::ReactNative::ReactPropertyBag(m_reactContext->Properties())); + if (useSyncCall) { + // Legacy behavior: wait for JS thread to finish executing (can cause deadlocks) + m_jsMessageThread.Load()->runOnQueueSync([]() {}); + } + // Default behavior: no synchronization call to prevent deadlocks } Mso::CntPtr MakeReactInstance(