mirror of
https://github.com/react/react-native.git
synced 2026-10-20 13:55:17 +08:00
Replace merge commit scheduling with a run loop observer (#56726)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56726 Changelog: [IOS][CHANGED] - Drain React-revision merges from a `BeforeWaiting` main run loop observer instead of dispatching each merge as a separate main-queue block With Fabric commit branching (`enableFabricCommitBranching`), React commits land on a forked `currentReactRevision_` and are merged into the main branch later via `ShadowTree::mergeReactRevision()`. On iOS, the `schedulerShouldMergeReactRevision:` callback used to do a plain `RCTExecuteOnMainQueue` per promotion, so every promotion enqueued a fresh main queue block that competed for ordering with mount blocks dispatched from concurrent commits with `mountSynchronously=true`. Instead of dispatching each merge as its own main-queue block, enqueue the surface id into an `unordered_set<SurfaceId>` and drain it from a `MainRunLoopObserver` registered at `kCFRunLoopBeforeWaiting`. The merge calls `ShadowTree::commit(..., mountSynchronously = true)`, so the mount completes inline before the observer returns. A similar mechanism has already been implemented on Android in D100966623 Reviewed By: javache Differential Revision: D104227839 fbshipit-source-id: 94953290fad3e65f038846ba81e05591756864a8
This commit is contained in:
committed by
meta-codesync[bot]
parent
c0bf1549c2
commit
d80377cd60
@@ -9,6 +9,7 @@
|
||||
|
||||
#import <mutex>
|
||||
#import <shared_mutex>
|
||||
#import <unordered_set>
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBridge+Private.h>
|
||||
@@ -42,8 +43,28 @@ using namespace facebook;
|
||||
using namespace facebook::react;
|
||||
|
||||
@interface RCTSurfacePresenter () <RCTSchedulerDelegate, RCTMountingManagerDelegate>
|
||||
- (void)_drainPendingReactRevisionMerges;
|
||||
@end
|
||||
|
||||
namespace {
|
||||
class ReactRevisionMergeRunLoopObserverDelegate final : public RunLoopObserver::Delegate {
|
||||
public:
|
||||
explicit ReactRevisionMergeRunLoopObserverDelegate(RCTSurfacePresenter *surfacePresenter)
|
||||
: surfacePresenter_(surfacePresenter)
|
||||
{
|
||||
}
|
||||
|
||||
void activityDidChange(const RunLoopObserver::Delegate * /*delegate*/, RunLoopObserver::Activity /*activity*/)
|
||||
const noexcept override
|
||||
{
|
||||
[surfacePresenter_ _drainPendingReactRevisionMerges];
|
||||
}
|
||||
|
||||
private:
|
||||
__weak RCTSurfacePresenter *surfacePresenter_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@implementation RCTSurfacePresenter {
|
||||
RCTMountingManager *_mountingManager; // Thread-safe.
|
||||
RCTSurfaceRegistry *_surfaceRegistry; // Thread-safe.
|
||||
@@ -57,6 +78,12 @@ using namespace facebook::react;
|
||||
|
||||
std::shared_mutex _observerListMutex;
|
||||
std::vector<__weak id<RCTSurfacePresenterObserver>> _observers; // Protected by `_observerListMutex`.
|
||||
|
||||
std::mutex _pendingReactRevisionMergesMutex;
|
||||
// Pending React revision merges, drained on the main run loop just before it sleeps.
|
||||
std::unordered_set<SurfaceId> _pendingReactRevisionMerges; // Protected by `_pendingReactRevisionMergesMutex`.
|
||||
std::shared_ptr<ReactRevisionMergeRunLoopObserverDelegate> _mergeRunLoopObserverDelegate;
|
||||
std::unique_ptr<const MainRunLoopObserver> _mergeRunLoopObserver;
|
||||
}
|
||||
|
||||
- (instancetype)initWithContextContainer:(std::shared_ptr<const ContextContainer>)contextContainer
|
||||
@@ -74,6 +101,14 @@ using namespace facebook::react;
|
||||
_mountingManager.contextContainer = contextContainer;
|
||||
_mountingManager.delegate = self;
|
||||
|
||||
if (ReactNativeFeatureFlags::enableFabricCommitBranching()) {
|
||||
_mergeRunLoopObserverDelegate = std::make_shared<ReactRevisionMergeRunLoopObserverDelegate>(self);
|
||||
_mergeRunLoopObserver = std::make_unique<const MainRunLoopObserver>(
|
||||
RunLoopObserver::Activity::BeforeWaiting, _mergeRunLoopObserverDelegate);
|
||||
_mergeRunLoopObserver->setDelegate(_mergeRunLoopObserverDelegate.get());
|
||||
_mergeRunLoopObserver->enable();
|
||||
}
|
||||
|
||||
_scheduler = [self _createScheduler];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
@@ -309,11 +344,49 @@ using namespace facebook::react;
|
||||
|
||||
- (void)schedulerShouldMergeReactRevision:(SurfaceId)surfaceId
|
||||
{
|
||||
auto scheduler = [self scheduler];
|
||||
RCTExecuteOnMainQueue(^{
|
||||
scheduler.uiManager->getShadowTreeRegistry().visit(
|
||||
surfaceId, [](const ShadowTree &shadowTree) { shadowTree.mergeReactRevision(); });
|
||||
});
|
||||
if (RCTIsMainQueue()) {
|
||||
[self _mergeReactRevisionForSurfaceId:surfaceId];
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_pendingReactRevisionMergesMutex);
|
||||
_pendingReactRevisionMerges.insert(surfaceId);
|
||||
}
|
||||
|
||||
- (void)_mergeReactRevisionForSurfaceId:(SurfaceId)surfaceId
|
||||
{
|
||||
RCTAssertMainQueue();
|
||||
RCTScheduler *scheduler = [self scheduler];
|
||||
if (!scheduler) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto uiManager = scheduler.uiManager;
|
||||
if (!uiManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiManager->getShadowTreeRegistry().visit(
|
||||
surfaceId, [](const ShadowTree &shadowTree) { shadowTree.mergeReactRevision(); });
|
||||
}
|
||||
|
||||
- (void)_drainPendingReactRevisionMerges
|
||||
{
|
||||
RCTAssertMainQueue();
|
||||
|
||||
std::unordered_set<SurfaceId> pending;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_pendingReactRevisionMergesMutex);
|
||||
if (_pendingReactRevisionMerges.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pending.swap(_pendingReactRevisionMerges);
|
||||
}
|
||||
|
||||
for (auto surfaceId : pending) {
|
||||
[self _mergeReactRevisionForSurfaceId:surfaceId];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)schedulerDidDispatchCommand:(const ShadowView &)shadowView
|
||||
|
||||
Reference in New Issue
Block a user