Skip to content

Commit

Permalink
Prevent redundant dispatches onto RuntimeExecutor queue in Asynchrono…
Browse files Browse the repository at this point in the history
…usEventBeat::induce

Summary:
Changelog: [internal]

Current implementation of `AsynchronousEventBeat` dispatches lambdas through `RuntimeExecutor` regardless if it has done so previously.

So if `AsynchronousEventBeat::induce` is called 30 times, it will dispatch 30 lambdas.

In `AsynchronousEventBeatV2`, we make sure only single lambda is dispatched to `RuntimeExecutor` at a time.

Reviewed By: mdvacca

Differential Revision: D27940300

fbshipit-source-id: 2bad25c86315c1712b4a1da8c1d4702734cec70f
  • Loading branch information
sammy-SC authored and facebook-github-bot committed Apr 24, 2021
1 parent 09cb12c commit 1b59263
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 2 deletions.
13 changes: 11 additions & 2 deletions React/Fabric/RCTSurfacePresenter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#import <react/renderer/core/LayoutConstraints.h>
#import <react/renderer/core/LayoutContext.h>
#import <react/renderer/scheduler/AsynchronousEventBeat.h>
#import <react/renderer/scheduler/AsynchronousEventBeatV2.h>
#import <react/renderer/scheduler/SchedulerToolbox.h>
#import <react/renderer/scheduler/SynchronousEventBeat.h>
#import <react/utils/ContextContainer.h>
Expand Down Expand Up @@ -305,10 +306,18 @@ - (RCTScheduler *)_createScheduler
return std::make_unique<SynchronousEventBeat>(std::move(runLoopObserver), runtimeExecutor);
};

toolbox.asynchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) {
auto enableV2AsynchronousEventBeat =
reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_asynchronous_event_beat_v2_ios");

toolbox.asynchronousEventBeatFactory = [runtimeExecutor, enableV2AsynchronousEventBeat](
EventBeat::SharedOwnerBox const &ownerBox) -> std::unique_ptr<EventBeat> {
auto runLoopObserver =
std::make_unique<MainRunLoopObserver const>(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner);
return std::make_unique<AsynchronousEventBeat>(std::move(runLoopObserver), runtimeExecutor);
if (enableV2AsynchronousEventBeat) {
return std::make_unique<AsynchronousEventBeatV2>(std::move(runLoopObserver), runtimeExecutor);
} else {
return std::make_unique<AsynchronousEventBeat>(std::move(runLoopObserver), runtimeExecutor);
}
};

RCTScheduler *scheduler = [[RCTScheduler alloc] initWithToolbox:toolbox];
Expand Down
58 changes: 58 additions & 0 deletions ReactCommon/react/renderer/scheduler/AsynchronousEventBeatV2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "AsynchronousEventBeatV2.h"

#include <react/debug/react_native_assert.h>

namespace facebook::react {

AsynchronousEventBeatV2::AsynchronousEventBeatV2(
RunLoopObserver::Unique uiRunLoopObserver,
RuntimeExecutor runtimeExecutor)
: EventBeat({}),
uiRunLoopObserver_(std::move(uiRunLoopObserver)),
runtimeExecutor_(std::move(runtimeExecutor)) {
uiRunLoopObserver_->setDelegate(this);
uiRunLoopObserver_->enable();
}

void AsynchronousEventBeatV2::activityDidChange(
RunLoopObserver::Delegate const *delegate,
RunLoopObserver::Activity) const noexcept {
react_native_assert(delegate == this);
induce();
}

void AsynchronousEventBeatV2::induce() const {
if (!isRequested_ || isBeatCallbackScheduled_) {
return;
}

isRequested_ = false;

// Here we know that `this` object exists because the caller has a strong
// pointer to `owner`. To ensure the object will exist inside
// `runtimeExecutor_` callback, we need to copy the pointer there.
auto weakOwner = uiRunLoopObserver_->getOwner();

isBeatCallbackScheduled_ = true;

runtimeExecutor_([this, weakOwner](jsi::Runtime &runtime) {
isBeatCallbackScheduled_ = false;

auto owner = weakOwner.lock();
if (!owner) {
return;
}

if (beatCallback_) {
beatCallback_(runtime);
}
});
}
} // namespace facebook::react
41 changes: 41 additions & 0 deletions ReactCommon/react/renderer/scheduler/AsynchronousEventBeatV2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <ReactCommon/RuntimeExecutor.h>
#include <react/renderer/core/EventBeat.h>
#include <react/utils/RunLoopObserver.h>

namespace facebook::react {

/*
* Event beat associated with JavaScript runtime.
* The beat is called on `RuntimeExecutor`'s thread induced by the UI thread
* event loop.
*/
class AsynchronousEventBeatV2 : public EventBeat,
public RunLoopObserver::Delegate {
public:
AsynchronousEventBeatV2(
RunLoopObserver::Unique uiRunLoopObserver,
RuntimeExecutor runtimeExecutor);

void induce() const override;

#pragma mark - RunLoopObserver::Delegate

void activityDidChange(
RunLoopObserver::Delegate const *delegate,
RunLoopObserver::Activity activity) const noexcept override;

private:
RunLoopObserver::Unique uiRunLoopObserver_;
RuntimeExecutor runtimeExecutor_;

mutable std::atomic<bool> isBeatCallbackScheduled_{false};
};

} // namespace facebook::react

0 comments on commit 1b59263

Please sign in to comment.