0

I'm using V8 to execute some custom javascript code, exposing OnUpdate function to JS world. Overall code works fine but currently I'm concerned about performance of below code - is it required to grab v8::Locker for executing any user defined function? Instruments.app shows code here spends way too much time in v8::Locker constructor and destructor -

v8 profile

90 ms (in actual code execution) vs ~4000ms (by Locker & ~Locker) - this is absurd and I feel I might be doing something wrong.

So my basic question is it really necessary to grab v8::Locker to execute a v8::Function::Call? In current state if I comment out v8::LockerI get below error message:

# Fatal error in HandleScope::HandleScope
# Entering the V8 API without proper locking in place

Code snippet:

int Bucket::send_doc_update_bucket(const char *msg) {
    Locker locker(GetIsolate());
    Isolate::Scope isolate_scope(GetIsolate());
    HandleScope handle_scope(GetIsolate());

    Local<Context> context = Local<Context>::New(GetIsolate(), context_);
    Context::Scope context_scope(context);

    TryCatch try_catch;

    Local<Value> args[1];
    args[0] = String::NewFromUtf8(GetIsolate(), msg);

    assert(!try_catch.HasCaught());

    Handle<Value> val;
    if(!context->Global()->Get(context,
                               createUtf8String(GetIsolate(),
                                                "OnUpdate")).ToLocal(&val) ||
      !val->IsFunction()) {
          return 3;
    }

    Handle<Function> on_doc_update = Handle<Function>::Cast(val);
    on_doc_update->Call(context, context->Global(), 1, args);

    if (try_catch.HasCaught()) {
        //w->last_exception = ExceptionString(GetIsolate(), &try_catch);
        return 2;
    }

    return 0;
 }
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Abhi
  • 33
  • 1
  • 4
  • 1
    If the code has any kind of multithreading, often locking apis get high usage amounts simply because they are blocking the thread from running when another is holding it, not because they are expensive but because they are doing their job – Yakk - Adam Nevraumont Jun 25 '16 at 18:40
  • Yakk, I understand your point - but it isn't clear if that's the case here. In this case, `v8::Locker` is bound to each `v8::Isolate` instance which allows multiple parallel code executions using `v8::Context` but I believe the way I'm using v8 - only one Context is running per Isolate. Link to the repo - https://github.com/abhi-bit/eventing/ – Abhi Jun 25 '16 at 19:15
  • You only need to create and destroy v8::Locker objects when you actually switch threads. If you are accessing your V8 isolate from the same OS thread all the time, then you can create a Locker and leave it active for a long time. I think GetIsolate() is still a fairly expensive call too, and you may want to cache the result in a local variable if you are calling it multiple times. – Erik Corry Jun 29 '16 at 08:49
  • Erik, thanks. I think, I understand the problem now in my case - Since I was pipelining mutations from Golang world to C++ v8 binding, Go scheduler was by default spinning up 8 OS level threads(have 8 logical cores) and then it mapping "goroutines" to one of them following "cooperative" scheduling. That's `v8::Locker` is getting hot even when JS function being executed is trivial - that probably explains the cputime usage pattern I shared originally. – Abhi Jul 01 '16 at 06:01
  • @Abhi I don't quite understand what you're saying in your first comment, but only one context in an isolate can run at a time. If you want multiple simultaneous threads, each context must be in a different isolate. The v8::Locker is explicitly to prevent multiple contexts from the same isolate from running at the same time. – xaxxon Sep 16 '16 at 05:14
  • Can anyone tell me where `GetIsolate()` is defined? In examples I found it's always some instance of `FunctionCallbackInfo` `GetIsolate` is called from.. – frans Aug 08 '19 at 05:05

1 Answers1

0

If you're not doing any multithreading, you never need to touch v8::Locker. As soon as you do, though, then you have to have it pretty much everywhere.

A v8::Locker is to stop multiple v8::Context's from the same v8::Isolate from running at the same time.

If you want multiple simultaneous threads of execution, each context must be created in a different isolate.

xaxxon
  • 19,189
  • 5
  • 50
  • 80