Skip to content

Skip non-owned subtrees in roundLayoutResultsToPixelGrid - #2015

Closed
zeyap wants to merge 1 commit into
react:mainfrom
zeyap:export-D117688567
Closed

Skip non-owned subtrees in roundLayoutResultsToPixelGrid#2015
zeyap wants to merge 1 commit into
react:mainfrom
zeyap:export-D117688567

Conversation

@zeyap

@zeyap zeyap commented Aug 27, 2026

Copy link
Copy Markdown

Summary:

Note

This PR description is AI-generated.

Fabric runs layout on candidate trees before taking the commit mutex, and concurrent commits share every unchanged subtree, so Yoga's pixel-grid rounding pass could write rounded positions and dimensions into the same shared yoga::Node from two threads at once. The layout pass itself never mutates nodes it does not own (Node::cloneChildrenIfNeeded), but roundLayoutResultsToPixelGrid recursed across the ownership frontier. I made the rounding recursion skip children whose owner is not the current node, mirroring the existing owner checks in Node::cloneChildrenIfNeeded and YGNodeFreeRecursive. The skipped writes had no reader: YogaLayoutableShadowNode::layout copies metrics only from children with hasNewLayout (asserting they are owned), hasNewLayout is set only on nodes the pass laid out, and the shadow nodes past the frontier are sealed, so their LayoutMetrics could not change in that commit anyway.

Changelog:

[GENERAL] [FIXED] - Fix data race between concurrent Fabric commits in Yoga's pixel-grid rounding pass

X-link: react/react-native#58144

Reviewed By: christophpurrer

Differential Revision: D117688567

Pulled By: zeyap

Summary:
> [!NOTE]
> **This PR description is AI-generated.**


Fabric runs layout on candidate trees before taking the commit mutex, and concurrent commits share every unchanged subtree, so Yoga's pixel-grid rounding pass could write rounded positions and dimensions into the same shared `yoga::Node` from two threads at once. The layout pass itself never mutates nodes it does not own (`Node::cloneChildrenIfNeeded`), but `roundLayoutResultsToPixelGrid` recursed across the ownership frontier. I made the rounding recursion skip children whose owner is not the current node, mirroring the existing owner checks in `Node::cloneChildrenIfNeeded` and `YGNodeFreeRecursive`. The skipped writes had no reader: `YogaLayoutableShadowNode::layout` copies metrics only from children with `hasNewLayout` (asserting they are owned), `hasNewLayout` is set only on nodes the pass laid out, and the shadow nodes past the frontier are sealed, so their `LayoutMetrics` could not change in that commit anyway.

## Changelog:

[GENERAL] [FIXED] - Fix data race between concurrent Fabric commits in Yoga's pixel-grid rounding pass

X-link: react/react-native#58144

Reviewed By: christophpurrer

Differential Revision: D117688567

Pulled By: zeyap
@meta-cla meta-cla Bot added the CLA Signed label Aug 27, 2026
@meta-codesync

meta-codesync Bot commented Aug 27, 2026

Copy link
Copy Markdown

@zeyap has exported this pull request. If you are a Meta employee, you can view the originating Diff in D117688567.

@meta-codesync

meta-codesync Bot commented Aug 28, 2026

Copy link
Copy Markdown

@zeyap merged this pull request in bd8fe0d.

meta-codesync Bot pushed a commit to react/react-native that referenced this pull request Aug 28, 2026
Summary:
X-link: react/yoga#2015

> [!NOTE]
> **This PR description is AI-generated.**

Fabric runs layout on candidate trees before taking the commit mutex, and concurrent commits share every unchanged subtree, so Yoga's pixel-grid rounding pass could write rounded positions and dimensions into the same shared `yoga::Node` from two threads at once. The layout pass itself never mutates nodes it does not own (`Node::cloneChildrenIfNeeded`), but `roundLayoutResultsToPixelGrid` recursed across the ownership frontier. I made the rounding recursion skip children whose owner is not the current node, mirroring the existing owner checks in `Node::cloneChildrenIfNeeded` and `YGNodeFreeRecursive`. The skipped writes had no reader: `YogaLayoutableShadowNode::layout` copies metrics only from children with `hasNewLayout` (asserting they are owned), `hasNewLayout` is set only on nodes the pass laid out, and the shadow nodes past the frontier are sealed, so their `LayoutMetrics` could not change in that commit anyway.

## Changelog:

[GENERAL] [FIXED] - Fix data race between concurrent Fabric commits in Yoga's pixel-grid rounding pass

Pull Request resolved: #58144

Test Plan: ThreadSanitizer reports of the race, from react-native-reanimated's sanitizer nightly (React commit on the JS thread racing a Reanimated commit on the main thread over a shared `ParagraphShadowNode`): https://github.com/software-mansion/react-native-reanimated/actions/runs/32811464205/job/97691453444

Reviewed By: christophpurrer

Differential Revision: D117688567

Pulled By: zeyap

fbshipit-source-id: 3f0662aee2ce865b4a13edffd46217f349a4285d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants