Fabric: Asserting if the RuntimeExecutor callback in RCTRuntimeExecutorFromBridge is not called

Summary:
The implementation of RuntimeExecutor must execute all provided callbacks. However, the implementation of RCTRuntimeExecutorFromBridge cannot guarantee it because it relies on Bridge to make the call. In this diff, we wrap the callback into a callable that asserts if it's not being called before destruction.

Changelog: [Internal] Fabric-specific internal change.

Reviewed By: mdvacca

Differential Revision: D22810439

fbshipit-source-id: f11932019ab6ccbab7e65db5919e0c64dcaf37ed
This commit is contained in:
Samuel Susla
2020-08-03 10:45:14 -07:00
committed by Facebook GitHub Bot
parent f838c7e5c3
commit a1b6f7bd9e
2 changed files with 102 additions and 1 deletions
@@ -18,6 +18,7 @@
#import <React/RCTSurfacePresenterStub.h>
#import <ReactCommon/RuntimeExecutor.h>
#import <react/utils/CalledOnceMovableOnlyFunction.h>
#import <react/utils/ContextContainer.h>
#import <react/utils/ManagedObjectWrapper.h>
@@ -48,7 +49,20 @@ static RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge)
auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge);
RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper](
std::function<void(facebook::jsi::Runtime & runtime)> &&callback) {
std::function<void(facebook::jsi::Runtime &)> &&callbackArgument) {
#ifndef NDEBUG
// Here we wrap callback into a callable that will assert if the `callback` is not called before deallocation
// or called more than once.
// It's useful to see at which exact point in time those assumptions were violated.
auto sharedCallback = std::make_shared<CalledOnceMovableOnlyFunction<void, facebook::jsi::Runtime &>>(
[callback = std::move(callbackArgument)](facebook::jsi::Runtime &runtime) { callback(runtime); });
auto callback = std::function<void(facebook::jsi::Runtime &)>(
[sharedCallback](facebook::jsi::Runtime &runtime) { (*sharedCallback)(runtime); });
#else
auto callback = std::move(callbackArgument);
#endif
RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper);
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call.");
@@ -0,0 +1,87 @@
/*
* 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 <functional>
namespace facebook {
namespace react {
/*
* Implements a moveable-only function that asserts if called more than once
* or destroyed before calling.
* Useful for use in debug mode to ensure such guarantees.
*/
template <typename ReturnT = void, typename... ArgumentT>
class CalledOnceMovableOnlyFunction {
using T = ReturnT(ArgumentT...);
std::function<T> function_;
bool wasCalled_;
bool wasMovedFrom_;
public:
CalledOnceMovableOnlyFunction(std::function<T> &&function)
: function_(std::move(function)) {
wasCalled_ = false;
wasMovedFrom_ = false;
}
~CalledOnceMovableOnlyFunction() {
assert(
(wasCalled_ || wasMovedFrom_) &&
"`CalledOnceMovableOnlyFunction` is destroyed before being called.");
}
/*
* Not copyable.
*/
CalledOnceMovableOnlyFunction(CalledOnceMovableOnlyFunction const &other) =
delete;
CalledOnceMovableOnlyFunction &operator=(
CalledOnceMovableOnlyFunction const &other) = delete;
/*
* Movable.
*/
CalledOnceMovableOnlyFunction(
CalledOnceMovableOnlyFunction &&other) noexcept {
wasCalled_ = false;
wasMovedFrom_ = false;
other.wasMovedFrom_ = true;
function_ = std::move(other.function_);
};
CalledOnceMovableOnlyFunction &operator=(
CalledOnceMovableOnlyFunction &&other) noexcept {
assert(
(wasCalled_ || wasMovedFrom_) &&
"`CalledOnceMovableOnlyFunction` is re-assigned before being called.");
wasCalled_ = false;
wasMovedFrom_ = false;
other.wasMovedFrom_ = true;
function_ = std::move(other.function_);
return *this;
}
/*
* Callable.
*/
ReturnT operator()(ArgumentT... args) {
assert(
!wasMovedFrom_ &&
"`CalledOnceMovableOnlyFunction` is called after being moved from.");
assert(
!wasCalled_ &&
"`CalledOnceMovableOnlyFunction` is called more than once.");
wasCalled_ = true;
return function_(args...);
}
};
} // namespace react
} // namespace facebook