diff --git a/include/hermes/BCGen/HBC/BCProviderFromSrc.h b/include/hermes/BCGen/HBC/BCProviderFromSrc.h index 9dc9ad94222..6f4af931055 100644 --- a/include/hermes/BCGen/HBC/BCProviderFromSrc.h +++ b/include/hermes/BCGen/HBC/BCProviderFromSrc.h @@ -10,6 +10,7 @@ #include "hermes/BCGen/HBC/BCProvider.h" #include "hermes/BCGen/HBC/Bytecode.h" +#include "hermes/Support/SerialExecutor.h" #include "llvh/ADT/Optional.h" @@ -97,6 +98,17 @@ class BCProviderFromSrc final : public BCProviderBase { /// variables/information. CompilationData compilationData_; + /// Use an 8MB stack, which is the default size on mac and linux. + static constexpr size_t kExecutorStackSize = 1 << 23; + + /// Idle for 1 second before letting the executor thread be cleaned up, + /// after which further tasks will start a new thread. + static constexpr std::chrono::milliseconds kExecutorTimeout = + std::chrono::milliseconds(1000); + + /// The executor used to run the compiler. + SerialExecutor serialExecutor_{kExecutorStackSize, kExecutorTimeout}; + /// The BytecodeModule that provides the bytecode data. /// Placed below CompilationData to ensure its destruction before the /// Module gets deleted. @@ -242,6 +254,10 @@ class BCProviderFromSrc final : public BCProviderBase { sourceHash_ = hash; }; + SerialExecutor &getSerialExecutor() { + return serialExecutor_; + } + static bool classof(const BCProviderBase *provider) { return provider->getKind() == BCProviderKind::BCProviderFromSrc; } diff --git a/lib/BCGen/HBC/HBC.cpp b/lib/BCGen/HBC/HBC.cpp index 7af2c18bc8a..52aa2b6a88d 100644 --- a/lib/BCGen/HBC/HBC.cpp +++ b/lib/BCGen/HBC/HBC.cpp @@ -15,9 +15,10 @@ #include "hermes/Support/PerfSection.h" #include "hermes/Support/SimpleDiagHandler.h" -#include "llvh/Support/Threading.h" #include "llvh/Support/raw_ostream.h" +#include + #define DEBUG_TYPE "hbc-backend" namespace hermes { @@ -66,6 +67,21 @@ std::unique_ptr generateBytecodeModuleForEval( namespace { +/// Execute a function in a SerialExecutor, blocking until the function +/// completes. +/// \param executor the executor to use. +/// \param f the function to execute. +/// \param data the data to pass to the function. +static void executeBlockingInSerialExecutor( + SerialExecutor &executor, + void (*f)(void *), + void *data) { + std::packaged_task task([f, data]() { f(data); }); + auto future = task.get_future(); + executor.add([&task]() { task(); }); + future.wait(); +} + /// Data for the compileLazyFunctionWorker. class LazyCompilationThreadData { public: @@ -344,12 +360,9 @@ std::pair compileLazyFunction( // Run on a thread to prevent stack overflow if this is run from deep inside // JS execution. - - // Use an 8MB stack, which is the default size on mac and linux. - constexpr unsigned kStackSize = 1 << 23; - LazyCompilationThreadData data{provider, funcID}; - llvh::llvm_execute_on_thread(compileLazyFunctionWorker, &data, kStackSize); + executeBlockingInSerialExecutor( + provider->getSerialExecutor(), compileLazyFunctionWorker, &data); if (data.success) { return std::make_pair(true, llvh::StringRef{}); @@ -412,12 +425,10 @@ std::pair, std::string> compileEvalModule( const CompileFlags &compileFlags) { // Run on a thread to prevent stack overflow if this is run from deep inside // JS execution. - - // Use an 8MB stack, which is the default size on mac and linux. - constexpr unsigned kStackSize = 1 << 23; - EvalThreadData data{std::move(src), provider, enclosingFuncID, compileFlags}; - llvh::llvm_execute_on_thread(compileEvalWorker, &data, kStackSize); + executeBlockingInSerialExecutor( + provider->getSerialExecutor(), compileEvalWorker, &data); + return data.success ? std::make_pair(std::move(data.result), "") : std::make_pair(std::unique_ptr{}, data.error);