Skip to content

Skip non-owned subtrees in roundLayoutResultsToPixelGrid - #58144

Open
tjzel wants to merge 1 commit into
react:mainfrom
tjzel:fix/yoga-pixel-grid-rounding-shared-nodes
Open

Skip non-owned subtrees in roundLayoutResultsToPixelGrid#58144
tjzel wants to merge 1 commit into
react:mainfrom
tjzel:fix/yoga-pixel-grid-rounding-shared-nodes

Conversation

@tjzel

@tjzel tjzel commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Note

This PR description is AI-generated.

Summary:

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

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

roundLayoutResultsToPixelGrid recurses into every yoga child and writes
rounded positions and dimensions back into each node. Unlike the layout
pass, which clones children before mutating them (cloneChildrenIfNeeded),
the rounding pass crosses the ownership frontier into subtrees that are
structurally shared with other shadow trees.

Under Fabric, two trees can run layout concurrently: commits run
layoutIfNeeded on candidate trees before taking the commit mutex, and
concurrent committers (React on the JS thread and a library committing
from another thread) share every unchanged subtree. Both rounding passes
then mutate the same shared yoga nodes. ThreadSanitizer reports this as
a data race: reads at PixelGrid.cpp:72/75 against writes at
PixelGrid.cpp:89/109 via Node::setLayoutPosition.

Skipping children whose owner is not the current node is safe:

- YogaLayoutableShadowNode::layout only copies metrics from children
  with hasNewLayout, and asserts those children are owned
  (YGNodeGetOwner(childYogaNode) == &yogaNode_).
- hasNewLayout is only set on nodes the pass performed layout on, so
  nodes past the ownership frontier are cache-restored, keep the flag
  unset, and the metrics-copying recursion never descends into them.
- The shadow nodes past the frontier were not cloned in this commit,
  so they are sealed and cannot accept new LayoutMetrics at all.

Rounded values written to non-owned nodes therefore have no reader; the
writes can only corrupt the state of other trees. The guard mirrors the
existing owner checks in Node::cloneChildrenIfNeeded and
YGNodeFreeRecursive ("Don't free shared nodes that we don't own").

Observed in react-native-reanimated's ThreadSanitizer nightly CI:
https://github.com/software-mansion/react-native-reanimated/actions/runs/32811464205/job/97691453444
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 25, 2026
@facebook-github-tools facebook-github-tools Bot added p: Software Mansion Partner: Software Mansion Partner labels Aug 25, 2026
@tjzel
tjzel marked this pull request as ready for review August 25, 2026 12:54
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Aug 25, 2026
@zeyap
zeyap self-requested a review August 25, 2026 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Software Mansion Partner: Software Mansion Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant