Files
react-native/ReactCommon/hermes/executor/HermesExecutorFactory.cpp
T
Dark Knight 39fb2cccb2 Revert D38242964: Multisect successfully blamed D38242964 for test or build failures
Summary:
This diff is reverting D38242964 (https://github.com/facebook/react-native/commit/d19cee34927c116b27b1513267bdf3ad20f1ea8d)
D38242964 (https://github.com/facebook/react-native/commit/d19cee34927c116b27b1513267bdf3ad20f1ea8d) has been identified to be causing the following test or build failures:
Tests affected:
- https://www.internalfb.com/intern/test/281475044166194/
- https://www.internalfb.com/intern/test/562950020887755/

Here's the Multisect link:
https://www.internalfb.com/intern/testinfra/multisect/1117138
Here are the tasks that are relevant to this breakage:
T101319872: 14 tests started failing for oncall ar_engine in the last 2 weeks
We're generating a revert to back out the changes in this diff, please note the backout may land if someone accepts it.

Changelog: [Internal]

Reviewed By: jpporto

Differential Revision: D38452063

fbshipit-source-id: 8d89fe742f04b60fcc110ab3163874ad5eb2af9a
2022-08-05 15:05:35 -07:00

228 lines
7.8 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "HermesExecutorFactory.h"
#include <thread>
#include <cxxreact/MessageQueueThread.h>
#include <cxxreact/SystraceSection.h>
#include <hermes/hermes.h>
#include <jsi/decorator.h>
#ifdef HERMES_ENABLE_DEBUGGER
#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/Registration.h>
#endif
#include "JSITracing.h"
using namespace facebook::hermes;
using namespace facebook::jsi;
namespace facebook {
namespace react {
namespace {
std::unique_ptr<HermesRuntime> makeHermesRuntimeSystraced(
const ::hermes::vm::RuntimeConfig &runtimeConfig) {
SystraceSection s("HermesExecutorFactory::makeHermesRuntimeSystraced");
return hermes::makeHermesRuntime(runtimeConfig);
}
#ifdef HERMES_ENABLE_DEBUGGER
class HermesExecutorRuntimeAdapter
: public facebook::hermes::inspector::RuntimeAdapter {
public:
HermesExecutorRuntimeAdapter(
std::shared_ptr<HermesRuntime> runtime,
std::shared_ptr<MessageQueueThread> thread)
: runtime_(runtime), thread_(std::move(thread)) {}
virtual ~HermesExecutorRuntimeAdapter() = default;
HermesRuntime &getRuntime() override {
return *runtime_;
}
void tickleJs() override {
// The queue will ensure that runtime_ is still valid when this
// gets invoked.
thread_->runOnQueue([&runtime = runtime_]() {
auto func =
runtime->global().getPropertyAsFunction(*runtime, "__tickleJs");
func.call(*runtime);
});
}
private:
std::shared_ptr<HermesRuntime> runtime_;
std::shared_ptr<MessageQueueThread> thread_;
};
#endif
struct ReentrancyCheck {
// This is effectively a very subtle and complex assert, so only
// include it in builds which would include asserts.
#ifndef NDEBUG
ReentrancyCheck() : tid(std::thread::id()), depth(0) {}
void before() {
std::thread::id this_id = std::this_thread::get_id();
std::thread::id expected = std::thread::id();
// A note on memory ordering: the main purpose of these checks is
// to observe a before/before race, without an intervening after.
// This will be detected by the compare_exchange_strong atomicity
// properties, regardless of memory order.
//
// For everything else, it is easiest to think of 'depth' as a
// proxy for any access made inside the VM. If access to depth
// are reordered incorrectly, the same could be true of any other
// operation made by the VM. In fact, using acquire/release
// memory ordering could create barriers which mask a programmer
// error. So, we use relaxed memory order, to avoid masking
// actual ordering errors. Although, in practice, ordering errors
// of this sort would be surprising, because the decorator would
// need to call after() without before().
if (tid.compare_exchange_strong(
expected, this_id, std::memory_order_relaxed)) {
// Returns true if tid and expected were the same. If they
// were, then the stored tid referred to no thread, and we
// atomically saved this thread's tid. Now increment depth.
assert(depth == 0 && "No thread id, but depth != 0");
++depth;
} else if (expected == this_id) {
// If the stored tid referred to a thread, expected was set to
// that value. If that value is this thread's tid, that's ok,
// just increment depth again.
assert(depth != 0 && "Thread id was set, but depth == 0");
++depth;
} else {
// The stored tid was some other thread. This indicates a bad
// programmer error, where VM methods were called on two
// different threads unsafely. Fail fast (and hard) so the
// crash can be analyzed.
__builtin_trap();
}
}
void after() {
assert(
tid.load(std::memory_order_relaxed) == std::this_thread::get_id() &&
"No thread id in after()");
if (--depth == 0) {
// If we decremented depth to zero, store no-thread into tid.
std::thread::id expected = std::this_thread::get_id();
bool didWrite = tid.compare_exchange_strong(
expected, std::thread::id(), std::memory_order_relaxed);
assert(didWrite && "Decremented to zero, but no tid write");
}
}
std::atomic<std::thread::id> tid;
// This is not atomic, as it is only written or read from the owning
// thread.
unsigned int depth;
#endif
};
// This adds ReentrancyCheck and debugger enable/teardown to the given
// Runtime.
class DecoratedRuntime : public jsi::WithRuntimeDecorator<ReentrancyCheck> {
public:
// The first argument may be another decorater which itself
// decorates the real HermesRuntime, depending on the build config.
// The second argument is the real HermesRuntime as well to
// manage the debugger registration.
DecoratedRuntime(
std::unique_ptr<Runtime> runtime,
HermesRuntime &hermesRuntime,
std::shared_ptr<MessageQueueThread> jsQueue)
: jsi::WithRuntimeDecorator<ReentrancyCheck>(*runtime, reentrancyCheck_),
runtime_(std::move(runtime)),
hermesRuntime_(hermesRuntime) {
#ifdef HERMES_ENABLE_DEBUGGER
std::shared_ptr<HermesRuntime> rt(runtime_, &hermesRuntime);
auto adapter = std::make_unique<HermesExecutorRuntimeAdapter>(rt, jsQueue);
facebook::hermes::inspector::chrome::enableDebugging(
std::move(adapter), "Hermes React Native");
#else
(void)hermesRuntime_;
#endif
}
~DecoratedRuntime() {
#ifdef HERMES_ENABLE_DEBUGGER
facebook::hermes::inspector::chrome::disableDebugging(hermesRuntime_);
#endif
}
private:
// runtime_ is a potentially decorated Runtime.
// hermesRuntime is a reference to a HermesRuntime managed by runtime_.
//
// HermesExecutorRuntimeAdapter requirements are kept, because the
// dtor will disable debugging on the HermesRuntime before the
// member managing it is destroyed.
std::shared_ptr<Runtime> runtime_;
ReentrancyCheck reentrancyCheck_;
HermesRuntime &hermesRuntime_;
};
} // namespace
std::unique_ptr<JSExecutor> HermesExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) {
std::unique_ptr<HermesRuntime> hermesRuntime =
makeHermesRuntimeSystraced(runtimeConfig_);
HermesRuntime &hermesRuntimeRef = *hermesRuntime;
auto decoratedRuntime = std::make_shared<DecoratedRuntime>(
std::move(hermesRuntime), hermesRuntimeRef, jsQueue);
// So what do we have now?
// DecoratedRuntime -> HermesRuntime
//
// DecoratedRuntime is held by JSIExecutor. When it gets used, it
// will check that it's on the right thread, do any necessary trace
// logging, then call the real HermesRuntime. When it is destroyed,
// it will shut down the debugger before the HermesRuntime is. In
// the normal case where debugging is not compiled in,
// all that's left is the thread checking.
// Add js engine information to Error.prototype so in error reporting we
// can send this information.
auto errorPrototype =
decoratedRuntime->global()
.getPropertyAsObject(*decoratedRuntime, "Error")
.getPropertyAsObject(*decoratedRuntime, "prototype");
errorPrototype.setProperty(*decoratedRuntime, "jsEngine", "hermes");
return std::make_unique<HermesExecutor>(
decoratedRuntime, delegate, jsQueue, timeoutInvoker_, runtimeInstaller_);
}
HermesExecutor::HermesExecutor(
std::shared_ptr<jsi::Runtime> runtime,
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue,
const JSIScopedTimeoutInvoker &timeoutInvoker,
RuntimeInstaller runtimeInstaller)
: JSIExecutor(runtime, delegate, timeoutInvoker, runtimeInstaller) {
jsi::addNativeTracingHooks(*runtime);
}
} // namespace react
} // namespace facebook