Skip to content

Commit a514ab2

Browse files
committed
fix(iOS): retain active Scheduler delegates and retire queued work
1 parent d7ff82e commit a514ab2

15 files changed

Lines changed: 730 additions & 99 deletions

‎packages/react-native/React/Fabric/RCTScheduler.mm‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,23 @@
2323

2424
class SchedulerDelegateProxy : public SchedulerDelegate {
2525
public:
26-
SchedulerDelegateProxy(void *scheduler) : scheduler_(scheduler) {}
26+
SchedulerDelegateProxy(RCTScheduler *scheduler) : scheduler_(scheduler) {}
2727

2828
void schedulerDidFinishTransaction(const std::shared_ptr<const MountingCoordinator> &mountingCoordinator) override
2929
{
30-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
30+
RCTScheduler *scheduler = scheduler_;
3131
[scheduler.delegate schedulerDidFinishTransaction:mountingCoordinator];
3232
}
3333

3434
void schedulerShouldRenderTransactions(const std::shared_ptr<const MountingCoordinator> &mountingCoordinator) override
3535
{
36-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
36+
RCTScheduler *scheduler = scheduler_;
3737
[scheduler.delegate schedulerShouldRenderTransactions:mountingCoordinator];
3838
}
3939

4040
void schedulerShouldMergeReactRevision(SurfaceId surfaceId) override
4141
{
42-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
42+
RCTScheduler *scheduler = scheduler_;
4343
[scheduler.delegate schedulerShouldMergeReactRevision:surfaceId];
4444
}
4545

@@ -54,28 +54,28 @@ void schedulerDidDispatchCommand(
5454
const std::string &commandName,
5555
const folly::dynamic &args) override
5656
{
57-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
57+
RCTScheduler *scheduler = scheduler_;
5858
[scheduler.delegate schedulerDidDispatchCommand:shadowView commandName:commandName args:args];
5959
}
6060

6161
void schedulerDidSetIsJSResponder(const ShadowView &shadowView, bool isJSResponder, bool blockNativeResponder)
6262
override
6363
{
64-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
64+
RCTScheduler *scheduler = scheduler_;
6565
[scheduler.delegate schedulerDidSetIsJSResponder:isJSResponder
6666
blockNativeResponder:blockNativeResponder
6767
forShadowView:shadowView];
6868
}
6969

7070
void schedulerDidSendAccessibilityEvent(const ShadowView &shadowView, const std::string &eventType) override
7171
{
72-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
72+
RCTScheduler *scheduler = scheduler_;
7373
[scheduler.delegate schedulerDidSendAccessibilityEvent:shadowView eventType:eventType];
7474
}
7575

7676
void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic &props) override
7777
{
78-
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
78+
RCTScheduler *scheduler = scheduler_;
7979
[scheduler.delegate schedulerDidSynchronouslyUpdateViewOnUIThread:tag props:props];
8080
}
8181

@@ -104,7 +104,7 @@ void schedulerDidClearPendingSnapshots() override
104104
}
105105

106106
private:
107-
void *scheduler_;
107+
__weak RCTScheduler *scheduler_;
108108
};
109109

110110
class LayoutAnimationDelegateProxy : public LayoutAnimationStatusDelegate, public RunLoopObserver::Delegate {
@@ -187,15 +187,15 @@ void pause() override
187187
@implementation RCTScheduler {
188188
std::unique_ptr<Scheduler> _scheduler;
189189
std::shared_ptr<LayoutAnimationDriver> _animationDriver;
190-
std::unique_ptr<SchedulerDelegateProxy> _delegateProxy;
190+
std::shared_ptr<SchedulerDelegateProxy> _delegateProxy;
191191
std::shared_ptr<LayoutAnimationDelegateProxy> _layoutAnimationDelegateProxy;
192192
std::unique_ptr<const PlatformRunLoopObserver> _uiRunLoopObserver;
193193
}
194194

195195
- (instancetype)initWithToolbox:(SchedulerToolbox)toolbox
196196
{
197197
if (self = [super init]) {
198-
_delegateProxy = std::make_unique<SchedulerDelegateProxy>((__bridge void *)self);
198+
_delegateProxy = std::make_shared<SchedulerDelegateProxy>(self);
199199

200200
if (ReactNativeFeatureFlags::enableLayoutAnimationsOnIOS()) {
201201
_layoutAnimationDelegateProxy = std::make_shared<LayoutAnimationDelegateProxy>((__bridge void *)self);
@@ -212,8 +212,8 @@ - (instancetype)initWithToolbox:(SchedulerToolbox)toolbox
212212
toolbox.animationChoreographer = std::make_shared<RCTAnimationChoreographer>();
213213
}
214214

215-
_scheduler = std::make_unique<Scheduler>(
216-
toolbox, (_animationDriver ? _animationDriver.get() : nullptr), _delegateProxy.get());
215+
_scheduler =
216+
std::make_unique<Scheduler>(toolbox, (_animationDriver ? _animationDriver.get() : nullptr), _delegateProxy);
217217
}
218218

219219
return self;

‎packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp‎

Lines changed: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "Scheduler.h"
99

10+
#include "SchedulerDelegateRegistration.h"
11+
1012
#include <glog/logging.h>
1113
#include <jsi/jsi.h>
1214

@@ -36,7 +38,27 @@ Scheduler::Scheduler(
3638
const SchedulerToolbox& schedulerToolbox,
3739
UIManagerAnimationDelegate* animationDelegate,
3840
SchedulerDelegate* delegate)
39-
: runtimeExecutor_(schedulerToolbox.runtimeExecutor),
41+
: Scheduler(
42+
schedulerToolbox,
43+
animationDelegate,
44+
std::make_shared<SchedulerDelegateRegistration>(delegate)) {}
45+
46+
Scheduler::Scheduler(
47+
const SchedulerToolbox& schedulerToolbox,
48+
UIManagerAnimationDelegate* animationDelegate,
49+
std::shared_ptr<SchedulerDelegate> delegate)
50+
: Scheduler(
51+
schedulerToolbox,
52+
animationDelegate,
53+
std::make_shared<SchedulerDelegateRegistration>(
54+
std::move(delegate))) {}
55+
56+
Scheduler::Scheduler(
57+
const SchedulerToolbox& schedulerToolbox,
58+
UIManagerAnimationDelegate* animationDelegate,
59+
std::shared_ptr<SchedulerDelegateRegistration> delegateRegistration)
60+
: delegateRegistration_(std::move(delegateRegistration)),
61+
runtimeExecutor_(schedulerToolbox.runtimeExecutor),
4062
contextContainer_(schedulerToolbox.contextContainer) {
4163
// Creating a container for future `EventDispatcher` instance.
4264
eventDispatcher_ = std::make_shared<std::optional<const EventDispatcher>>();
@@ -156,7 +178,6 @@ Scheduler::Scheduler(
156178
std::weak_ptr<const ComponentDescriptorRegistry>(
157179
componentDescriptorRegistry_));
158180

159-
delegate_ = delegate;
160181
commitHooks_ = schedulerToolbox.commitHooks;
161182

162183
// Layout events (`onLayout`) are emitted as a standalone consumer of the
@@ -185,6 +206,8 @@ Scheduler::Scheduler(
185206
}
186207

187208
Scheduler::~Scheduler() {
209+
std::atomic_load(&delegateRegistration_)->retire();
210+
188211
LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this
189212
<< ").";
190213

@@ -278,11 +301,23 @@ Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
278301
#pragma mark - Delegate
279302

280303
void Scheduler::setDelegate(SchedulerDelegate* delegate) {
281-
delegate_ = delegate;
304+
replaceDelegate(std::make_shared<SchedulerDelegateRegistration>(delegate));
305+
}
306+
307+
void Scheduler::setOwnedDelegate(std::shared_ptr<SchedulerDelegate> delegate) {
308+
replaceDelegate(
309+
std::make_shared<SchedulerDelegateRegistration>(std::move(delegate)));
310+
}
311+
312+
void Scheduler::replaceDelegate(
313+
std::shared_ptr<SchedulerDelegateRegistration> delegateRegistration) {
314+
auto previous = std::atomic_exchange(
315+
&delegateRegistration_, std::move(delegateRegistration));
316+
previous->retire();
282317
}
283318

284319
SchedulerDelegate* Scheduler::getDelegate() const {
285-
return delegate_;
320+
return std::atomic_load(&delegateRegistration_)->acquire().get();
286321
}
287322

288323
#pragma mark - UIManagerAnimationDelegate
@@ -301,29 +336,32 @@ void Scheduler::uiManagerDidFinishTransaction(
301336
bool mountSynchronously) {
302337
TraceSection s("Scheduler::uiManagerDidFinishTransaction");
303338

304-
if (delegate_ != nullptr) {
339+
auto registration = std::atomic_load(&delegateRegistration_);
340+
if (auto delegate = registration->acquire()) {
305341
// This is no-op on all platforms except for Android where we need to
306342
// observe each transaction to be able to mount correctly.
307-
delegate_->schedulerDidFinishTransaction(mountingCoordinator);
343+
delegate->schedulerDidFinishTransaction(mountingCoordinator);
308344

309345
if (!mountSynchronously) {
310346
auto surfaceId = mountingCoordinator->getSurfaceId();
311347

312348
runtimeScheduler_->scheduleRenderingUpdate(
313349
surfaceId,
314-
[delegate = delegate_,
350+
[registration = std::move(registration),
315351
mountingCoordinator = std::move(mountingCoordinator)]() {
316-
delegate->schedulerShouldRenderTransactions(mountingCoordinator);
352+
if (auto delegate = registration->acquire()) {
353+
delegate->schedulerShouldRenderTransactions(mountingCoordinator);
354+
}
317355
});
318356
} else {
319-
delegate_->schedulerShouldRenderTransactions(mountingCoordinator);
357+
delegate->schedulerShouldRenderTransactions(mountingCoordinator);
320358
}
321359
}
322360
}
323361

324362
void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode& shadowNode) {
325-
if (delegate_ != nullptr) {
326-
delegate_->schedulerDidRequestPreliminaryViewAllocation(shadowNode);
363+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
364+
delegate->schedulerDidRequestPreliminaryViewAllocation(shadowNode);
327365
}
328366
}
329367

@@ -333,15 +371,19 @@ void Scheduler::uiManagerDidDispatchCommand(
333371
const folly::dynamic& args) {
334372
TraceSection s(
335373
"Scheduler::uiManagerDispatchCommand", "commandName", commandName);
336-
if (delegate_ != nullptr) {
374+
auto registration = std::atomic_load(&delegateRegistration_);
375+
if (registration->acquire()) {
337376
auto shadowView = ShadowView(*shadowNode);
338377
runtimeScheduler_->scheduleRenderingUpdate(
339378
shadowNode->getSurfaceId(),
340-
[delegate = delegate_,
379+
[registration = std::move(registration),
341380
shadowView = std::move(shadowView),
342381
commandName,
343382
args]() {
344-
delegate->schedulerDidDispatchCommand(shadowView, commandName, args);
383+
if (auto delegate = registration->acquire()) {
384+
delegate->schedulerDidDispatchCommand(
385+
shadowView, commandName, args);
386+
}
345387
});
346388
}
347389
}
@@ -351,9 +393,9 @@ void Scheduler::uiManagerDidSendAccessibilityEvent(
351393
const std::string& eventType) {
352394
TraceSection s("Scheduler::uiManagerDidSendAccessibilityEvent");
353395

354-
if (delegate_ != nullptr) {
396+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
355397
auto shadowView = ShadowView(*shadowNode);
356-
delegate_->schedulerDidSendAccessibilityEvent(shadowView, eventType);
398+
delegate->schedulerDidSendAccessibilityEvent(shadowView, eventType);
357399
}
358400
}
359401

@@ -364,45 +406,45 @@ void Scheduler::uiManagerDidSetIsJSResponder(
364406
const std::shared_ptr<const ShadowNode>& shadowNode,
365407
bool isJSResponder,
366408
bool blockNativeResponder) {
367-
if (delegate_ != nullptr) {
368-
delegate_->schedulerDidSetIsJSResponder(
409+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
410+
delegate->schedulerDidSetIsJSResponder(
369411
ShadowView(*shadowNode), isJSResponder, blockNativeResponder);
370412
}
371413
}
372414

373415
void Scheduler::uiManagerShouldSynchronouslyUpdateViewOnUIThread(
374416
Tag tag,
375417
const folly::dynamic& props) {
376-
if (delegate_ != nullptr) {
377-
delegate_->schedulerShouldSynchronouslyUpdateViewOnUIThread(tag, props);
418+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
419+
delegate->schedulerShouldSynchronouslyUpdateViewOnUIThread(tag, props);
378420
}
379421
}
380422

381423
void Scheduler::uiManagerDidUpdateShadowTree(
382424
const std::unordered_map<Tag, folly::dynamic>& tagToProps) {
383-
if (delegate_ != nullptr) {
384-
delegate_->schedulerDidUpdateShadowTree(tagToProps);
425+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
426+
delegate->schedulerDidUpdateShadowTree(tagToProps);
385427
}
386428
}
387429

388430
void Scheduler::uiManagerDidCaptureViewSnapshot(Tag tag, SurfaceId surfaceId) {
389-
if (delegate_ != nullptr) {
390-
delegate_->schedulerDidCaptureViewSnapshot(tag, surfaceId);
431+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
432+
delegate->schedulerDidCaptureViewSnapshot(tag, surfaceId);
391433
}
392434
}
393435

394436
void Scheduler::uiManagerDidSetViewSnapshot(
395437
Tag sourceTag,
396438
Tag targetTag,
397439
SurfaceId surfaceId) {
398-
if (delegate_ != nullptr) {
399-
delegate_->schedulerDidSetViewSnapshot(sourceTag, targetTag, surfaceId);
440+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
441+
delegate->schedulerDidSetViewSnapshot(sourceTag, targetTag, surfaceId);
400442
}
401443
}
402444

403445
void Scheduler::uiManagerDidClearPendingSnapshots() {
404-
if (delegate_ != nullptr) {
405-
delegate_->schedulerDidClearPendingSnapshots();
446+
if (auto delegate = std::atomic_load(&delegateRegistration_)->acquire()) {
447+
delegate->schedulerDidClearPendingSnapshots();
406448
}
407449
}
408450

@@ -417,13 +459,21 @@ void Scheduler::uiManagerShouldRemoveEventListener(
417459
}
418460

419461
void Scheduler::uiManagerDidFinishReactCommit(const ShadowTree& shadowTree) {
420-
if (delegate_ == nullptr) {
462+
auto registration = std::atomic_load(&delegateRegistration_);
463+
if (!registration->acquire()) {
421464
return;
422465
}
423466

424467
auto surfaceId = shadowTree.getSurfaceId();
425468
runtimeScheduler_->scheduleRenderingUpdate(
426-
surfaceId, [surfaceId, uiManager = uiManager_, delegate = delegate_]() {
469+
surfaceId,
470+
[surfaceId,
471+
uiManager = uiManager_,
472+
registration = std::move(registration)]() {
473+
auto delegate = registration->acquire();
474+
if (!delegate) {
475+
return;
476+
}
427477
bool promoted = false;
428478

429479
uiManager->getShadowTreeRegistry().visit(

‎packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
namespace facebook::react {
3535

36+
class SchedulerDelegateRegistration;
3637
class CdpMetricsReporter;
3738
class CdpPerfIssuesReporter;
3839
class EventPerformanceLogger;
@@ -48,6 +49,10 @@ class Scheduler final : public UIManagerDelegate {
4849
const SchedulerToolbox &schedulerToolbox,
4950
UIManagerAnimationDelegate *animationDelegate,
5051
SchedulerDelegate *delegate);
52+
Scheduler(
53+
const SchedulerToolbox &schedulerToolbox,
54+
UIManagerAnimationDelegate *animationDelegate,
55+
std::shared_ptr<SchedulerDelegate> delegate);
5156
~Scheduler() override;
5257

5358
#pragma mark - Surface Management
@@ -68,11 +73,19 @@ class Scheduler final : public UIManagerDelegate {
6873
#pragma mark - Delegate
6974

7075
/*
71-
* Sets and gets the Scheduler's delegate.
72-
* If you requesting a ComponentDescriptor and unsure that it's there, you are
73-
* doing something wrong.
76+
* Each assignment starts a new delegate generation and cancels queued work
77+
* for the previous generation. Already active callbacks may finish.
78+
* Lifecycle mutations (replacement and destruction) must be serialized by
79+
* the caller. Already acquired delegate invocations may overlap retirement.
80+
* Calls into Scheduler itself still require the caller to keep it alive;
81+
* only deferred rendering callbacks are independent of Scheduler's lifetime.
82+
* A borrowed delegate must outlive its active callbacks. The owned form
83+
* keeps it alive until those callbacks return.
84+
* getDelegate() is a non-owning snapshot; callers must synchronize its use
85+
* with replacement and destruction.
7486
*/
7587
void setDelegate(SchedulerDelegate *delegate);
88+
void setOwnedDelegate(std::shared_ptr<SchedulerDelegate> delegate);
7689
SchedulerDelegate *getDelegate() const;
7790

7891
#pragma mark - UIManagerAnimationDelegate
@@ -127,7 +140,13 @@ class Scheduler final : public UIManagerDelegate {
127140
private:
128141
friend class SurfaceHandler;
129142

130-
SchedulerDelegate *delegate_;
143+
Scheduler(
144+
const SchedulerToolbox &schedulerToolbox,
145+
UIManagerAnimationDelegate *animationDelegate,
146+
std::shared_ptr<SchedulerDelegateRegistration> delegateRegistration);
147+
void replaceDelegate(std::shared_ptr<SchedulerDelegateRegistration> delegateRegistration);
148+
149+
std::shared_ptr<SchedulerDelegateRegistration> delegateRegistration_;
131150
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
132151
RuntimeExecutor runtimeExecutor_;
133152
std::shared_ptr<UIManager> uiManager_;

0 commit comments

Comments
 (0)