Skip to content

Find a LongLivedObject in its collection without walking it - #59403

Merged
javache merged 2 commits into
react:mainfrom
hryhoriiK97:bridging-longlivedobject-remove-lookup
Oct 28, 2026
Merged

javache merged 2 commits into
react:mainfrom
hryhoriiK97:bridging-longlivedobject-remove-lookup

Conversation

@hryhoriiK97

Copy link
Copy Markdown
Contributor

Summary:

LongLivedObjectCollection (ReactCommon/react/bridging/LongLivedObject.cpp) keeps the JS callbacks and promises of native module calls alive until they're released: callbacks and promises of Objective-C TurboModules on iOS, AsyncCallback and AsyncPromise in C++ TurboModules, and LayoutAnimation callbacks. remove found the object by walking the whole std::unordered_set and comparing each pointer, so releasing one of N pending objects cost O(N), and releasing all of them O(N²).

The set holds std::shared_ptr<LongLivedObject>, and std::shared_ptr hashes and compares by the pointer it stores. So remove can look the object up with find, using a non-owning shared_ptr that points to it (the aliasing constructor with an empty owner, so nothing is allocated and no reference count changes). The same entry is erased under the same lock; removing an object that isn't in the set still does nothing.

In normal use only a few objects are pending at a time, so this matters when many native calls are pending at once: for example Promise.all over a large number of native calls, or a native module whose answers pile up while its queue is busy.

Changelog:

[GENERAL] [CHANGED] - Releasing a native module callback or promise no longer walks all the pending ones

Test Plan:

  • clang-format --dry-run --Werror (the repository's pinned version) on the changed file passes.
  • A standalone program built with clang++ -std=c++20 -O2 against LongLivedObject.cpp adds 2,000 objects, removes an object that isn't in the collection (the size stays the same), then calls allowRelease() on all of them in random order and checks the size and that each object is destroyed after each call. It passes before and after this change.
  • The same program timed allowRelease() with N objects in the collection, on an M-series Mac:
Pending before this PR
10 146 ns 50 ns
100 140 ns 40 ns
1,000 431 ns 38 ns
5,000 2.1 µs 48 ns
20,000 35 µs 55 ns
  • Measurement on device. RNTester Playground starting N calls of NativeSampleTurboModule.getValueWithPromise (or getValueWithCallback) at once and awaiting all of them, timed with performance.now(), median of 5–100 rounds per N. Two Release builds, before and after this change, installed in the order before, after, after, before, 4 launches each, on a physical iPhone 15 Pro (iOS 26.6.1). The presses were driven by Argent. Mean time per call:
Pending at once promises: before this PR change callbacks: before this PR change
100 5.54 µs 5.43 µs 2.74 µs 2.73 µs
500 4.00 µs 3.77 µs 2.93 µs 2.70 µs
1,000 4.31 µs 3.57 µs −17% 3.21 µs 2.70 µs −16%
5,000 10.40 µs 3.81 µs −63% 7.14 µs 2.89 µs −60%

At 100 and 500 the ranges of the two arms overlap. With this change, the time per call no longer grows with the number of pending calls.

  • I did not run the react/bridging gtest suite (BridgingTest); its tear-down checks that the collection is empty after each test, which covers remove.

🤖 Generated with Claude Code

LongLivedObjectCollection::remove walked the whole set to find the
object, so releasing N pending callbacks or promises cost O(N^2). Look it
up with a non-owning shared_ptr instead, which hashes and compares by the
same pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@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 Oct 23, 2026
@facebook-github-tools facebook-github-tools Bot added 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. labels Oct 23, 2026
Address review: make it explicit that the key is a non-owning alias.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@meta-codesync

meta-codesync Bot commented Oct 28, 2026

Copy link
Copy Markdown

@javache has imported this pull request. If you are a Meta employee, you can view this in D128033820.

@javache
javache merged commit caf9cbc into react:main Oct 28, 2026
105 of 107 checks passed
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.

2 participants