0

I am trying to use v8 in C++, I want to compile the js code in main thread and then I pass the compiled_script to the executor(child Thread). the Run execution process will run in threads.

Story: compile process is expensive and I need performance, therefore I will cache all compiled script at the beginning and will pass them to different threads for execution.

for testing this scenario I just create only one thread and passed the compiled script to it, after 1500 iteration in for loop, the program exited with Segmentation Fault Error.

The problem is related to thread, if I move the execution to outside the thread, it will successfully ended.

I investigate more but can not find any solutions.

Complete Code :

#include "include/libplatform/libplatform.h"
#include "include/v8.h"
#include <future>
#include <iostream>
#include <string>
#include <thread>

bool ExecuteScript(v8::Isolate *isolate,
                   v8::MaybeLocal<v8::Script> compiled_script) {

  v8::HandleScope handle_scope(isolate);
  v8::TryCatch try_catch(isolate);
  v8::Local<v8::Context> context(isolate->GetCurrentContext());

  v8::Local<v8::Value> result;
  v8::Local<v8::Script> c = compiled_script.FromMaybe(v8::Local<v8::Script>());
  if (c.IsEmpty())
    std::cout << "Compile Script is Empty" << std::endl;
  if (context.IsEmpty())
    std::cout << "Conetxt is Empty" << std::endl;
  auto x = c->Run(context);
  if (!x.ToLocal(&result)) {
    v8::String::Utf8Value error(isolate, try_catch.Exception());
    std::cout << *error << std::endl;
    return false;
  }
  return true;
}

v8::MaybeLocal<v8::Script> CompileScript(v8::Isolate *isolate,
                                         v8::Local<v8::String> raw_source) {
  v8::ScriptCompiler::Source source(raw_source);
  auto unboundedScript =
      v8::ScriptCompiler::CompileUnboundScript(
          isolate, &source,
          v8::ScriptCompiler::CompileOptions::kNoCompileOptions)
          .ToLocalChecked();

  return unboundedScript->BindToCurrentContext();
}

int main() {
  v8::Isolate::CreateParams create_params;
  v8::Isolate *isolate;
  std::unique_ptr<v8::Platform> platform;
  v8::Global<v8::Context> context_;

  v8::V8::InitializeICUDefaultLocation(".");
  v8::V8::InitializeExternalStartupData(".");
  platform = v8::platform::NewDefaultPlatform();
  v8::V8::InitializePlatform(platform.get());
  v8::V8::Initialize();

  create_params.array_buffer_allocator =
      v8::ArrayBuffer::Allocator::NewDefaultAllocator();
  isolate = v8::Isolate::New(create_params);
  v8::Isolate::Scope isolate_scope(isolate);
  v8::HandleScope scope(isolate);

  v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);

  v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
  context_.Reset(isolate, context);

  {

    auto tt = std::async(std::launch::async, [&]() {
      v8::Locker locker(isolate);
      v8::HandleScope thread_scope(isolate);
      v8::Context::Scope context_scope(context);
      v8::MaybeLocal<v8::Script> compliedScript = CompileScript(
          isolate,
          v8::String::NewFromUtf8Literal(isolate, "var j=0; while(j<5) j++; "));
      for (int i = 0; i < 1000000; i++) {
        std::cout << i << std::endl;
        if (!ExecuteScript(isolate, compliedScript)) {
          std::cout << "Execution Error" << std::endl;
          return;
        }
      }
    });
    tt.wait();
  }

  v8::V8::Dispose();
  v8::V8::ShutdownPlatform();
  delete create_params.array_buffer_allocator;
  std::cout << "Exit Normal" << std::endl;
  return 0;
}

How can I fix this ?

Edit1:

  1. Code Cleanup
  2. valgrind memory leak result :
==2588106== Warning: set address range perms: large range [0x2bf200000000, 0x2bf400000000) (noaccess)
==2588106== Warning: set address range perms: large range [0x2bf200000000, 0x2bf400000000) (noaccess)
==2588106== Warning: set address range perms: large range [0x2bf200000000, 0x2bf300000000) (noaccess)==2588106== Thread 9:
==2588106== Invalid read of size 8
==2588106==    at 0x2D7B24: v8::internal::Isolate::main_thread_local_heap() (in /home/linux/v8)
==2588106==    by 0x3F07EA: v8::internal::interpreter::BytecodeArrayIterator::BytecodeArrayIterator(v8::internal::Handle<v8::internal::BytecodeArray>, int) (in /home/linux/v8)
==2588106==    by 0xD7EC2B: v8::internal::compiler::BytecodeGraphBuilder::BytecodeGraphBuilder(v8::internal::compiler::JSHeapBroker*, v8::internal::Zone*, v8::internal::compiler::NativeContextRef const&, v8::internal::compiler::SharedFunctionInfoRef const&, v8::internal::compiler::FeedbackCellRef const&, v8::internal::BytecodeOffset, v8::internal::compiler::JSGraph*, v8::internal::compiler::CallFrequency const&, v8::internal::compiler::SourcePositionTable*, int, v8::internal::CodeKind, v8::base::Flags<v8::internal::compiler::BytecodeGraphBuilderFlag, int>, v8::internal::TickCounter*, v8::internal::compiler::ObserveNodeInfo const&) (in /home/linux/v8)
==2588106==    by 0xD92621: v8::internal::compiler::BuildGraphFromBytecode(v8::internal::compiler::JSHeapBroker*, v8::internal::Zone*, v8::internal::compiler::SharedFunctionInfoRef const&, v8::internal::compiler::FeedbackCellRef const&, v8::internal::BytecodeOffset, v8::internal::compiler::JSGraph*, v8::internal::compiler::CallFrequency const&, v8::internal::compiler::SourcePositionTable*, int, v8::internal::CodeKind, v8::base::Flags<v8::internal::compiler::BytecodeGraphBuilderFlag, int>, v8::internal::TickCounter*, v8::internal::compiler::ObserveNodeInfo const&) (in /home/linux/v8)
==2588106==    by 0x80E6FD: v8::internal::compiler::GraphBuilderPhase::Run(v8::internal::compiler::PipelineData*, v8::internal::Zone*) (in /home/linux/v8)
==2588106==    by 0x7FFE81: void v8::internal::compiler::PipelineImpl::Run<v8::internal::compiler::GraphBuilderPhase>() (in /home/linux/v8)
==2588106==    by 0x7FC747: v8::internal::compiler::PipelineImpl::CreateGraph() (in /home/linux/v8)
==2588106==    by 0x7FC10F: v8::internal::compiler::PipelineCompilationJob::PrepareJobImpl(v8::internal::Isolate*) (in /home/linux/v8)
==2588106==    by 0x253924: v8::internal::OptimizedCompilationJob::PrepareJob(v8::internal::Isolate*) (in /home/linux/v8)
==2588106==    by 0x25F4E5: v8::internal::(anonymous namespace)::GetOptimizedCodeLater(std::unique_ptr<v8::internal::OptimizedCompilationJob, std::default_delete<v8::internal::OptimizedCompilationJob> >, v8::internal::Isolate*, v8::internal::OptimizedCompilationInfo*, v8::internal::CodeKind, v8::internal::Handle<v8::internal::JSFunction>) (in /home/linux/v8)
==2588106==    by 0x25862D: v8::internal::(anonymous namespace)::GetOptimizedCode(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode, v8::internal::CodeKind, v8::internal::BytecodeOffset, v8::internal::JavaScriptFrame*) (in /home/linux/v8)
==2588106==    by 0x259905: v8::internal::Compiler::CompileOptimized(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode, v8::internal::CodeKind) (in /home/linux/v8)
==2588106==  Address 0xc1f8 is not stack'd, malloc'd or (recently) free'd
==2588106==
==2588106==
==2588106==  Access not within mapped region at address 0xC1F8
==2588106==    at 0x2D7B24: v8::internal::Isolate::main_thread_local_heap() (in /home/linux/v8)
==2588106==    by 0x3F07EA: v8::internal::interpreter::BytecodeArrayIterator::BytecodeArrayIterator(v8::internal::Handle<v8::internal::BytecodeArray>, int) (in /home/linux/v8)
==2588106==    by 0xD7EC2B: v8::internal::compiler::BytecodeGraphBuilder::BytecodeGraphBuilder(v8::internal::compiler::JSHeapBroker*, v8::internal::Zone*, v8::internal::compiler::NativeContextRef const&, v8::internal::compiler::SharedFunctionInfoRef const&, v8::internal::compiler::FeedbackCellRef const&, v8::internal::BytecodeOffset, v8::internal::compiler::JSGraph*, v8::internal::compiler::CallFrequency const&, v8::internal::compiler::SourcePositionTable*, int, v8::internal::CodeKind, v8::base::Flags<v8::internal::compiler::BytecodeGraphBuilderFlag, int>, v8::internal::TickCounter*, v8::internal::compiler::ObserveNodeInfo const&) (in /home/linux/v8)
==2588106==    by 0xD92621: v8::internal::compiler::BuildGraphFromBytecode(v8::internal::compiler::JSHeapBroker*, v8::internal::Zone*, v8::internal::compiler::SharedFunctionInfoRef const&, v8::internal::compiler::FeedbackCellRef const&, v8::internal::BytecodeOffset, v8::internal::compiler::JSGraph*, v8::internal::compiler::CallFrequency const&, v8::internal::compiler::SourcePositionTable*, int, v8::internal::CodeKind, v8::base::Flags<v8::internal::compiler::BytecodeGraphBuilderFlag, int>, v8::internal::TickCounter*, v8::internal::compiler::ObserveNodeInfo const&) (in /home/linux/v8)
==2588106==    by 0x80E6FD: v8::internal::compiler::GraphBuilderPhase::Run(v8::internal::compiler::PipelineData*, v8::internal::Zone*) (in /home/linux/v8)
==2588106==    by 0x7FFE81: void v8::internal::compiler::PipelineImpl::Run<v8::internal::compiler::GraphBuilderPhase>() (in /home/linux/v8)
==2588106==    by 0x7FC747: v8::internal::compiler::PipelineImpl::CreateGraph() (in /home/linux/v8)
==2588106==    by 0x7FC10F: v8::internal::compiler::PipelineCompilationJob::PrepareJobImpl(v8::internal::Isolate*) (in /home/linux/v8)
==2588106==    by 0x253924: v8::internal::OptimizedCompilationJob::PrepareJob(v8::internal::Isolate*) (in /home/linux/v8)
==2588106==    by 0x25F4E5: v8::internal::(anonymous namespace)::GetOptimizedCodeLater(std::unique_ptr<v8::internal::OptimizedCompilationJob, std::default_delete<v8::internal::OptimizedCompilationJob> >, v8::internal::Isolate*, v8::internal::OptimizedCompilationInfo*, v8::internal::CodeKind, v8::internal::Handle<v8::internal::JSFunction>) (in /home/linux/v8)
==2588106==    by 0x25862D: v8::internal::(anonymous namespace)::GetOptimizedCode(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode, v8::internal::CodeKind, v8::internal::BytecodeOffset, v8::internal::JavaScriptFrame*) (in /home/linux/v8)
==2588106==    by 0x259905: v8::internal::Compiler::CompileOptimized(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode, v8::internal::CodeKind) (in /home/linux/v8)
==2588106==  If you believe this happened as a result of a stack
==2588106==  overflow in your program's main thread (unlikely but
==2588106==  possible), you can try to increase the size of the
==2588106==  main thread stack using the --main-stacksize= flag.
==2588106==  The main thread stack size used in this run was 8388608.
==2588106==
==2588106== HEAP SUMMARY:
==2588106==     in use at exit: 541,595 bytes in 1,411 blocks
==2588106==   total heap usage: 2,072 allocs, 661 frees, 1,155,883 bytes allocated
==2588106==
==2588106== Thread 1:
==2588106== 1 bytes in 1 blocks are still reachable in loss record 1 of 1,252
==2588106==    at 0x483CFE3: operator new(unsigned long) (vg_replace_malloc.c:417)
==2588106==    by 0x330BFC: v8::internal::Heap::SetUpSpaces() (in /home/linux/v8)
==2588106==    by 0x2D60B7: v8::internal::Isolate::Init(v8::internal::SnapshotData*, v8::internal::SnapshotData*, bool) (in /home/linux/v8)
==2588106==    by 0x2D6C88: v8::internal::Isolate::InitWithSnapshot(v8::internal::SnapshotData*, v8::internal::SnapshotData*, bool) (in /home/linux/v8)
==2588106==    by 0x5F789D: v8::internal::Snapshot::Initialize(v8::internal::Isolate*) (in /home/linux/v8)
==2588106==    by 0x245612: v8::Isolate::Initialize(v8::Isolate*, v8::Isolate::CreateParams const&) (in /home/linux/v8)
==2588106==    by 0x24581C: v8::Isolate::New(v8::Isolate::CreateParams const&) (in /home/linux/v8)
==2588106==    by 0x2074BB: main (in /home/linux/v8)
==2588106==
==2588106== 1 bytes in 1 blocks are still reachable in loss record 2 of 1,252
==2588106==    at 0x483CFE3: operator new(unsigned long) (vg_replace_malloc.c:417)
==2588106==    by 0x330F6A: v8::internal::Heap::SetUpSpaces() (in /home/linux/v8)
==2588106==    by 0x2D60B7: v8::internal::Isolate::Init(v8::internal::SnapshotData*, v8::internal::SnapshotData*, bool) (in /home/linux/v8)
==2588106==    by 0x2D6C88: v8::internal::Isolate::InitWithSnapshot(v8::internal::SnapshotData*, v8::internal::SnapshotData*, bool) (in /home/linux/v8)
==2588106==    by 0x5F789D: v8::internal::Snapshot::Initialize(v8::internal::Isolate*) (in /home/linux/v8)
==2588106==    by 0x245612: v8::Isolate::Initialize(v8::Isolate*, v8::Isolate::CreateParams const&) (in /home/linux/v8)
==2588106==    by 0x24581C: v8::Isolate::New(v8::Isolate::CreateParams const&) (in /home/linux/v8)
==2588106==    by 0x2074BB: main (in /home/linux/v8)
==2588106==
==2588106== 1 bytes in 1 blocks are still reachable in loss record 3 of 1,252
==2588106==    at 0x483EA5D: operator new[](unsigned long, std::nothrow_t const&) (vg_replace_malloc.c:658)
==2588106==    by 0x53082E: v8::internal::String::ToCString(v8::internal::AllowNullsFlag, v8::internal::RobustnessFlag, int, int, int*) (in /home/linux/v8)
==2588106==    by 0x530C0A: v8::internal::String::ToCString(v8::internal::AllowNullsFlag, v8::internal::RobustnessFlag, int*) (in /home/linux/v8)
==2588106==    by 0x51AE2F: v8::internal::SharedFunctionInfo::DebugNameCStr() (in /home/linux/v8)
==2588106==    by 0x27839C: v8::internal::OptimizedCompilationInfo::GetDebugName() const (in /home/linux/v8)
==2588106==    by 0x7FB419: v8::internal::compiler::PipelineData::PipelineData(v8::internal::compiler::ZoneStats*, v8::internal::Isolate*, v8::internal::OptimizedCompilationInfo*, v8::internal::compiler::PipelineStatistics*, bool) (in /home/linux/v8)
==2588106==    by 0x7FB1FF: v8::internal::compiler::PipelineCompilationJob::PipelineCompilationJob(v8::internal::Isolate*, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::BytecodeOffset, v8::internal::JavaScriptFrame*, v8::internal::CodeKind) (in /home/linux/v8)
==2588106==    by 0x807EDA: v8::internal::compiler::Pipeline::NewCompilationJob(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::CodeKind, bool, v8::internal::BytecodeOffset, v8::internal::JavaScriptFrame*) (in /home/linux/v8)
==2588106==    by 0x2585F8: v8::internal::(anonymous namespace)::GetOptimizedCode(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode, v8::internal::CodeKind, v8::internal::BytecodeOffset, v8::internal::JavaScriptFrame*) (in /home/linux/v8)
==2588106==    by 0x259905: v8::internal::Compiler::CompileOptimized(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode, v8::internal::CodeKind) (in /home/linux/v8)
==2588106==    by 0xB2C785: v8::internal::(anonymous namespace)::CompileOptimized(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::ConcurrencyMode) (in /home/linux/v8)
==2588106==    by 0xB28ACB: v8::internal::Runtime_CompileOptimized_Concurrent(int, unsigned long*, v8::internal::Isolate*) (in /home/linux/v8)
==2588106==
==2588106== 2 bytes in 1 blocks are still reachable in loss record 4 of 1,252
==2588106==    at 0x483EA5D: operator new[](unsigned long, std::nothrow_t const&) (vg_replace_malloc.c:658)
==2588106==    by 0x261383: v8::internal::CallDescriptors::InitializeOncePerProcess() (in /home/linux/v8)
==2588106==    by 0x3F0386: v8::internal::V8::InitializeOncePerProcessImpl() (in /home/linux/v8)
==2588106==    by 0x9ED455: v8::base::CallOnceImpl(std::atomic<unsigned char>*, std::function<void ()>) (in /home/linux/v8)
==2588106==    by 0x3EFFFD: v8::internal::V8::Initialize() (in /home/linux/v8)
==2588106==    by 0x23634A: v8::V8::Initialize(int) (in /home/linux/v8)
==2588106==    by 0x2074A2: main (in /home/linux/v8)
==2588106==
==2588106== 2 bytes in 1 blocks are still reachable in loss record 5 of 1,252
==2588106==    at 0x483EA5D: operator new[](unsigned long, std::nothrow_t const&) (vg_replace_malloc.c:658)
==2588106==    by 0x2620B0: v8::internal::CallDescriptors::InitializeOncePerProcess() (in /home/linux/v8)
==2588106==    by 0x3F0386: v8::internal::V8::InitializeOncePerProcessImpl() (in /home/linux/v8)
==2588106==    by 0x9ED455: v8::base::CallOnceImpl(std::atomic<unsigned char>*, std::function<void ()>) (in /home/linux/v8)
==2588106==    by 0x3EFFFD: v8::internal::V8::Initialize() (in /home/linux/v8)
==2588106==    by 0x23634A: v8::V8::Initialize(int) (in /home/linux/v8)
==2588106==    by 0x2074A2: main (in /home/linux/v8)

Edit 2: Compiling command :

g++ sample.cpp libv8_monolith.a -I/usr/local/include/v8/ -I/usr/local/include/v8/include/ -lpthread -o v8 -DV8_COMPRESS_POINTERS

Mohsen
  • 59
  • 1
  • 8
  • *I investigate more but can not find any solutions.* -- The first thing you should do is figure out *why* the segmentation fault is occurring, instead of moving code around and hoping something works. – PaulMcKenzie Sep 22 '21 at 13:49
  • Yap you are right, sorry, I forget to put the my valgrind memory check output. – Mohsen Sep 22 '21 at 14:08
  • (1) Please post repro details: which V8 version (git hash), how did you compile (both V8 and your app). -- (2) Note that this approach won't scale to several threads anyways: the `v8::Locker` (which is required!) ensures that only one thread can use the `Isolate` at a time. -- (3) What makes you think that `CompileScript` is slow? I'd expect it to be very fast. – jmrk Sep 22 '21 at 15:31
  • (1) - v8 version is 9.1.0.0, (2) - Yes, I know, but in this example I just create only one thread and used `v8::Locker` (not necessary in this code block), see the `main()`, (3) - in my real case I have around 10 scripts and I got so many requests (thousands) from one of my C++ Object that needs to `Run` those scripts, and want to revoke compiling time during the execution, it significantly improve the performance. – Mohsen Sep 22 '21 at 17:32

1 Answers1

1

This was a bug in V8 9.1 version and it has been fixed by google.

Mohsen
  • 59
  • 1
  • 8
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 06 '22 at 13:03
  • What's the first version that includes the fix? – Ruben Sep 24 '22 at 22:50