From 2cfbdd7183bf74a360f67465a4bdf112afdae809 Mon Sep 17 00:00:00 2001 From: Gregory Moskaliuk Date: Fri, 23 Oct 2026 14:55:49 +0200 Subject: [PATCH 1/2] Find a LongLivedObject in its collection without walking it 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 --- .../ReactCommon/react/bridging/LongLivedObject.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp b/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp index 9c6d7c38d86f..41717d530341 100644 --- a/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp +++ b/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp @@ -36,11 +36,13 @@ void LongLivedObjectCollection::add(std::shared_ptr so) { void LongLivedObjectCollection::remove(const LongLivedObject* o) { std::scoped_lock lock(collectionMutex_); - for (auto p = collection_.begin(); p != collection_.end(); p++) { - if (p->get() == o) { - collection_.erase(p); - break; - } + // A shared_ptr hashes and compares by the pointer it stores, so a + // non-owning shared_ptr to `o` finds its entry without walking the set. + auto entry = collection_.find( + std::shared_ptr( + std::shared_ptr(), const_cast(o))); + if (entry != collection_.end()) { + collection_.erase(entry); } } From 282efcb778aff2740a18142f94f683d38336e24b Mon Sep 17 00:00:00 2001 From: Gregory Moskaliuk Date: Fri, 23 Oct 2026 20:03:56 +0200 Subject: [PATCH 2/2] Name the lookup key in LongLivedObjectCollection::remove Address review: make it explicit that the key is a non-owning alias. Co-Authored-By: Claude Opus 5.5 --- .../ReactCommon/react/bridging/LongLivedObject.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp b/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp index 41717d530341..aa4b1fcdb589 100644 --- a/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp +++ b/packages/react-native/ReactCommon/react/bridging/LongLivedObject.cpp @@ -36,11 +36,13 @@ void LongLivedObjectCollection::add(std::shared_ptr so) { void LongLivedObjectCollection::remove(const LongLivedObject* o) { std::scoped_lock lock(collectionMutex_); + // Empty owner: lookup-only alias, with no ownership or control block. + const std::shared_ptr lookupKey{ + std::shared_ptr{}, const_cast(o)}; + // A shared_ptr hashes and compares by the pointer it stores, so a // non-owning shared_ptr to `o` finds its entry without walking the set. - auto entry = collection_.find( - std::shared_ptr( - std::shared_ptr(), const_cast(o))); + auto entry = collection_.find(lookupKey); if (entry != collection_.end()) { collection_.erase(entry); }