Skip to content

Commit

Permalink
Move StackRuntime into Runtime.cpp
Browse files Browse the repository at this point in the history
Summary:
Our current `StackRuntime` setup lives at the API level, in
`hermes.cpp`, and involves an additional derived class also called
`StackRuntime` that lives in `Runtime.cpp` in order to work properly.
We can instead consolidate all the logic in `Runtime.cpp` and hide the
allocation location of the `Runtime` behind `Runtime::create`. This
will also make it more straightforward to implement a heap allocated
runtime.

Reviewed By: kodafb

Differential Revision: D32347126

fbshipit-source-id: 8b3b9505b0fe8cac44389e4eee2bf34ded6f803d
  • Loading branch information
neildhar authored and facebook-github-bot committed Jan 7, 2022
1 parent 4b8de5c commit 2c66a23
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 120 deletions.
92 changes: 5 additions & 87 deletions API/hermes/hermes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@

#include "llvh/Support/Compiler.h"

#if defined(HERMES_FACEBOOK_BUILD) && !defined(HERMES_FBCODE_BUILD)
// TODO (T84179835): Disable this once it is no longer useful for debugging.
#define HERMESJSI_ON_STACK
#endif

#include "hermes/BCGen/HBC/BytecodeDataProvider.h"
#include "hermes/BCGen/HBC/BytecodeFileFormat.h"
#include "hermes/BCGen/HBC/BytecodeProviderFromSrc.h"
Expand Down Expand Up @@ -56,11 +51,6 @@
#include <system_error>
#include <unordered_map>

#ifdef HERMESJSI_ON_STACK
#include <future>
#include <thread>
#endif

#include <jsi/instrumentation.h>
#include <jsi/threadsafe.h>

Expand Down Expand Up @@ -190,63 +180,6 @@ class InstallHermesFatalErrorHandler {
}
};

#ifdef HERMESJSI_ON_STACK
// Minidumps include stack memory, not heap memory. If we want to be
// able to inspect the Runtime object in a minidump, we can do that by
// arranging for it to be allocated on a stack. No existing stack is
// a good candidate, so we achieve this by creating a thread just to
// hold the Runtime.

class StackRuntime {
public:
StackRuntime(const vm::RuntimeConfig &runtimeConfig)
: thread_(runtimeMemoryThread, this) {
startup_.get_future().wait();
runtime_->emplace(runtimeConfig.rebuild()
.withRegisterStack(nullptr)
.withMaxNumRegisters(kMaxNumRegisters)
.build());
}

~StackRuntime() {
// We can't shut down the Runtime on the captive thread, because
// it might need to make JNI calls to clean up HostObjects. So we
// delete it from here, which is going to be on a thread
// registered with the JVM.
runtime_->reset();
shutdown_.set_value();
thread_.join();
runtime_ = nullptr;
}

::hermes::vm::Runtime &getRuntime() {
return **runtime_;
}

private:
static void runtimeMemoryThread(StackRuntime *stack) {
::hermes::oscompat::set_thread_name("hermes-runtime-memorythread");

llvh::Optional<::hermes::vm::StackRuntime> rt;

stack->runtime_ = &rt;
stack->startup_.set_value();
stack->shutdown_.get_future().wait();
assert(!rt.hasValue() && "Runtime was not torn down before thread");
}

// The order here matters.
// * Set up the promises
// * Initialize various pointers to null
// * Start the thread which uses them
// * Initialize provider_ and runtime_ from that thread
std::promise<void> startup_;
std::promise<void> shutdown_;
llvh::Optional<::hermes::vm::StackRuntime> *runtime_{nullptr};
std::thread thread_;
};
#endif

} // namespace

// Recording timing stats for every JS<->C++ transition has some overhead, so
Expand All @@ -267,18 +200,12 @@ class HermesRuntimeImpl final : public HermesRuntime,
static constexpr uint32_t kSentinelNativeValue = 0x6ef71fe1;

HermesRuntimeImpl(const vm::RuntimeConfig &runtimeConfig)
:
#ifdef HERMESJSI_ON_STACK
stackRuntime_(runtimeConfig),
runtime_(stackRuntime_.getRuntime()),
#else
rt_(::hermes::vm::Runtime::create(
: rt_(::hermes::vm::Runtime::create(
runtimeConfig.rebuild()
.withRegisterStack(nullptr)
.withMaxNumRegisters(kMaxNumRegisters)
.build())),
runtime_(*rt_),
#endif
vmExperimentFlags_(runtimeConfig.getVMExperimentFlags()),
crashMgr_(runtimeConfig.getCrashMgr()) {
compileFlags_.optimize = false;
Expand All @@ -304,11 +231,8 @@ class HermesRuntimeImpl final : public HermesRuntime,
compileFlags_.enableGenerator = runtimeConfig.getEnableGenerator();
compileFlags_.emitAsyncBreakCheck = defaultEmitAsyncBreakCheck_ =
runtimeConfig.getAsyncBreakCheckInEval();

#ifndef HERMESJSI_ON_STACK
// Register the memory for the runtime if it isn't stored on the stack.
// Register the memory for the runtime.
crashMgr_->registerMemory(&runtime_, sizeof(vm::Runtime));
#endif
runtime_.addCustomRootsFunction(
[this](vm::GC *, vm::RootAcceptor &acceptor) {
for (auto it = hermesValues_->begin(); it != hermesValues_->end();) {
Expand Down Expand Up @@ -374,10 +298,8 @@ class HermesRuntimeImpl final : public HermesRuntime,
// This must be done before we check hermesValues_ below.
debugger_.reset();
#endif
#ifndef HERMESJSI_ON_STACK
// Unregister the memory for the runtime if it isn't stored on the stack.
// Unregister the memory for the runtime.
crashMgr_->unregisterMemory(&runtime_);
#endif
}

#ifdef HERMES_ENABLE_DEBUGGER
Expand Down Expand Up @@ -1059,11 +981,7 @@ class HermesRuntimeImpl final : public HermesRuntime,
public:
ManagedValues<HermesPointerValue> hermesValues_;
ManagedValues<WeakRefPointerValue> weakHermesValues_;
#ifdef HERMESJSI_ON_STACK
StackRuntime stackRuntime_;
#else
std::shared_ptr<::hermes::vm::Runtime> rt_;
#endif
::hermes::vm::Runtime &runtime_;
#ifdef HERMES_ENABLE_DEBUGGER
friend class debugger::Debugger;
Expand Down Expand Up @@ -2019,7 +1937,7 @@ jsi::Value HermesRuntimeImpl::call(
hvFromValue(jsThis)};
if (LLVM_UNLIKELY(newFrame.overflowed())) {
checkStatus(runtime_.raiseStackOverflow(
::hermes::vm::StackRuntime::StackOverflowKind::NativeStack));
::hermes::vm::Runtime::StackOverflowKind::NativeStack));
}

for (uint32_t i = 0; i != count; ++i) {
Expand Down Expand Up @@ -2085,7 +2003,7 @@ jsi::Value HermesRuntimeImpl::callAsConstructor(
objHandle.getHermesValue()};
if (newFrame.overflowed()) {
checkStatus(runtime_.raiseStackOverflow(
::hermes::vm::StackRuntime::StackOverflowKind::NativeStack));
::hermes::vm::Runtime::StackOverflowKind::NativeStack));
}
for (uint32_t i = 0; i != count; ++i) {
newFrame->getArgRef(i) = hvFromValue(args[i]);
Expand Down
28 changes: 5 additions & 23 deletions include/hermes/VM/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -936,14 +936,6 @@ class Runtime : public HandleRootOwner,
void getInlineCacheProfilerInfo(llvh::raw_ostream &ostream);
#endif

protected:
/// Construct a Runtime on the stack.
/// NOTE: This should only be used by StackRuntime. All other uses should use
/// Runtime::create.
explicit Runtime(
std::shared_ptr<StorageProvider> provider,
const RuntimeConfig &runtimeConfig);

#if defined(HERMESVM_PROFILER_EXTERN)
public:
#else
Expand All @@ -953,6 +945,10 @@ class Runtime : public HandleRootOwner,
CallResult<HermesValue> interpretFunctionImpl(CodeBlock *newCodeBlock);

private:
explicit Runtime(
std::shared_ptr<StorageProvider> provider,
const RuntimeConfig &runtimeConfig);

/// Called by the GC at the beginning of a collection. This method informs the
/// GC of all runtime roots. The \p markLongLived argument
/// indicates whether root data structures that contain only
Expand Down Expand Up @@ -1133,6 +1129,7 @@ class Runtime : public HandleRootOwner,
friend class ScopedNativeDepthTracker;
friend class ScopedNativeCallFrame;

class StackRuntime;
class MarkRootsPhaseTimer;

/// Whenever we pass through the first phase, we record the current time here,
Expand Down Expand Up @@ -1481,21 +1478,6 @@ class Runtime : public HandleRootOwner,
std::unique_ptr<StackTracesTree> stackTracesTree_;
};

/// StackRuntime is meant to be used whenever a Runtime should be allocated on
/// the stack. This should only be used by JSI, everything else should use the
/// default creator.
class StackRuntime final : public Runtime {
public:
StackRuntime(const RuntimeConfig &config);
StackRuntime(
std::shared_ptr<StorageProvider> provider,
const RuntimeConfig &config);

// A dummy virtual destructor to avoid problems when StackRuntime is used
// in compilation units compiled with RTTI.
virtual ~StackRuntime();
};

/// An RAII class for automatically tracking the native call frame depth.
class ScopedNativeDepthTracker {
Runtime *const runtime_;
Expand Down
61 changes: 51 additions & 10 deletions lib/VM/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
#include "llvh/ADT/DenseMap.h"
#endif

#include <future>

namespace hermes {
namespace vm {

Expand All @@ -74,10 +76,59 @@ static const Predefined::Str fixedPropCacheNames[(size_t)PropCacheID::_COUNT] =

} // namespace

// Minidumps include stack memory, not heap memory. If we want to be
// able to inspect the Runtime object in a minidump, we can do that by
// arranging for it to be allocated on a stack. No existing stack is
// a good candidate, so we achieve this by creating a thread just to
// hold the Runtime.
class Runtime::StackRuntime {
public:
StackRuntime(const vm::RuntimeConfig &runtimeConfig)
: thread_(runtimeMemoryThread, this) {
startup_.get_future().get();
new (runtime_) Runtime(StorageProvider::mmapProvider(), runtimeConfig);
}

~StackRuntime() {
runtime_->~Runtime();
shutdown_.set_value();
thread_.join();
}

static std::shared_ptr<Runtime> create(const RuntimeConfig &runtimeConfig) {
auto srt = std::make_shared<StackRuntime>(runtimeConfig);
return std::shared_ptr<Runtime>(srt, srt->runtime_);
}

private:
static void runtimeMemoryThread(StackRuntime *stack) {
::hermes::oscompat::set_thread_name("hermes-runtime-memorythread");
std::aligned_storage<sizeof(Runtime)>::type rt;
stack->runtime_ = reinterpret_cast<Runtime *>(&rt);
stack->startup_.set_value();
stack->shutdown_.get_future().get();
}

// The order here matters.
// * Set up the promises
// * Initialize runtime_ to null
// * Start the thread which uses them
// * Initialize runtime_ from that thread
std::promise<void> startup_;
std::promise<void> shutdown_;
Runtime *runtime_{nullptr};
std::thread thread_;
};

/* static */
std::shared_ptr<Runtime> Runtime::create(const RuntimeConfig &runtimeConfig) {
#if defined(HERMES_FACEBOOK_BUILD) && !defined(HERMES_FBCODE_BUILD)
// TODO (T84179835): Disable this once it is no longer useful for debugging.
return StackRuntime::create(runtimeConfig);
#else
return std::shared_ptr<Runtime>{
new Runtime(StorageProvider::mmapProvider(), runtimeConfig)};
#endif
}

CallResult<PseudoHandle<>> Runtime::getNamed(
Expand Down Expand Up @@ -1693,16 +1744,6 @@ void Runtime::dumpCallFrames() {
dumpCallFrames(llvh::errs());
}

StackRuntime::StackRuntime(const RuntimeConfig &config)
: StackRuntime(StorageProvider::mmapProvider(), config) {}

StackRuntime::StackRuntime(
std::shared_ptr<StorageProvider> provider,
const RuntimeConfig &config)
: Runtime(std::move(provider), config) {}

StackRuntime::~StackRuntime() {}

/// Serialize a SymbolID.
llvh::raw_ostream &operator<<(
llvh::raw_ostream &OS,
Expand Down

0 comments on commit 2c66a23

Please sign in to comment.