4

I'm trying to call a callback in V8 from another point of my function. So this piece of code register the callback:

        if (args.Length())
        {
            String::Utf8Value event(args[0]->ToString());
            if (event.length())
            {  
                Isolate* isolate = V8Interface::getCurrent()->getIsolate();

                Locker locker(isolate);
                HandleScope scope(isolate);

                callback cb = callback(isolate, Local<Function>::Cast(args[1]));

                 if(!events.count(*event))
                 {
                events[*event] = callbacks({ cb });
                 } 
                 else 
                {
                    events.find(*event)->second.push_back(cb);
                 }
            }
        }

And this one calls it:

 void trigger(std::string event)
    {

        Isolate* isolate = V8Interface::getCurrent()->getIsolate();

        Locker locker(isolate);
        HandleScope scope(isolate);

        if(events.count(event))
        {
            for(callback cb : events.find(event)->second)
            {
                Local<Function> local = Local<Function>::New(isolate, cb);
                local->Call(isolate->GetCurrentContext()->Global(), 0, {});
            }
        }
    }

The trigger function can be called anywhere at any time in my C++ code. I tried a simple example (init v8 then call trigger), and I get:

#
# Fatal error in C:\OgreSDK\Projects\whitedrop\third_party\io.js\deps\v8\src/api.h, line 386
# CHECK(allow_empty_handle || that != 0) failed
#

This is my main:

Scribe::V8Interface v8Interface = Scribe::V8Interface();
v8Interface.initialize();

for(Whitedrop::WorldEvent* event : Whitedrop::worldEvents)
{
    event->onStart();
}

You can get the whole source here:

https://github.com/whitedrop/whitedrop/tree/feature/v8

Line 386 is:

/**
   * Create a local handle for the content of another handle.
   * The referee is kept alive by the local handle even when
   * the original handle is destroyed/disposed.
   */
  V8_INLINE static Local<T> New(Isolate* isolate, Handle<T> that); // <<<<<<
  V8_INLINE static Local<T> New(Isolate* isolate,
                                const PersistentBase<T>& that);

EDIT: I tried, thanks to Ben Noordhuis, like this:

Isolate* isolate = V8Interface::getCurrent()->getIsolate();

Locker locker(isolate);
HandleScope scope(isolate);

callback cb;
cb.Reset(isolate, Local<Function>::Cast(args[1]));

And to call:

Isolate* isolate = V8Interface::getCurrent()->getIsolate();

Locker locker(isolate);
HandleScope scope(isolate);

if(events.count(event))
{
    for(callback cb : events.find(event)->second)
    {
        Local<Function> local = Local<Function>::New(isolate, cb);
        local->Call(isolate->GetCurrentContext()->Global(), 0, {});
    }
}

But same error :'(

Vinz243
  • 9,654
  • 10
  • 42
  • 86
  • Is that code executing on the main thread? – mscdex Jan 25 '15 at 14:44
  • I think so. I'll upload the main loop. Anyway, I did nothing to implement threading – Vinz243 Jan 25 '15 at 14:45
  • The error indicates that your Isolate doesn't know about your Persistent. Is node triggering the callback from a new thread? Find out by calling v8::Isolate::SetData() in your addWorldEvent() function, then checking that data is still there in your trigger() function. – Brad Werth Feb 05 '15 at 21:09
  • @BradWerth I ddn't implemented threading, as I said. And how to use setData? – Vinz243 Feb 06 '15 at 11:43
  • node may be creating threads for you and calling your setup function from there. You can detect this by comparing the Isolates you are using in your addWorldEvent() function and your trigger() function. Are they the same object? I'm not sure the v8 handle would be the same even if they are the same object, so you can test it by setting data isolate->SetData((void*)1L) and then checking in trigger if(isolate->GetData() == (void *)1L). – Brad Werth Feb 06 '15 at 18:48
  • I'll try in a week, won't be there for long ;) – Vinz243 Feb 06 '15 at 21:57
  • The pointer ref the same address and the data set was the same after trial – Vinz243 Feb 15 '15 at 12:04

2 Answers2

3

Not sure wether author of question still need the answer. Anyway.

In continuing of previous answer about converting Local handlers to Persistent. There are some examples on Node.js C++ addons docs page:
https://nodejs.org/api/addons.html#addons_function_factory

For persistent objects they use static constructor method as Persistent<Function>. When module is inited constructor could be initialized by Reset method like this (actually i didn't find it's description in v8 docs - http://izs.me/v8-docs/classv8_1_1Persistent.html):

// some where in external class or as global var
static Persistent<Function> persistentCallback;

// when need to init
persistentCallback.Reset(isolate, f_tmpl->GetFunction());

To invoke this method when it's needed you should use Local handle cast like this:

Local<Function> f = Local<Function>::New(isoloate,persistentCallback)

Be aware you can't use Handle pointers, because its ::New method doesn't fit for such arguments.

BTW. I figured out that I don't need to use Persistent handler in my code to store callback method in case I has already bound it inside JS context. It seems that it's properly managed by GC in that case. But there is no other way to share vars between c++ functions calls, for ex. when you store some protected data inside you module.

Kreees
  • 106
  • 1
  • 3
0

I think you should cast Local<T> to Persistent<T> before storing them into events. If you don't the GC from v8 may collect the functions at any time.

Handle<Function> args0 = Handle<Function>::Cast(args[0]);
Persistent<Function> pfn(args0);
callback cb = callback(isolate, pfn); // use Persistent<T> instead of Local<T>
justin.m.chase
  • 13,061
  • 8
  • 52
  • 100
  • I get `error C2664: cannot convert argument 1 from 'v8::Handle' to 'const v8::Persistent> &` on your second line – Vinz243 Feb 23 '15 at 17:45
  • how about `Persistent pfn = Persistent::New(args0);` We may be using different versions, examples online seem to differ based on your version of v8. – justin.m.chase Feb 23 '15 at 22:41
  • OK v8.h is https://chromium.googlesource.com/v8/v8/+/4.1.0.14/include/v8.h#690 but I get ` cannot convert from 'v8::Primitive *' to 'v8::Object *volatile '` – Vinz243 Feb 24 '15 at 17:19
  • You may have to change the signature of the callback function to accept Handle instead of Local and anywhere where you want to store a reference convert it to a Persistent. The idea is that Local only has a reference to the object in the local scope. When the object goes out of scope and is dereferenced it will go on the list of objects for the GC to clean up. This can cause any Handle's or Local's that you are storing to end up pointing at an invalid location in memory at some time in the future. I would need a better code snippet to be able to help you further. – justin.m.chase Feb 24 '15 at 20:00
  • Here it is https://github.com/whitedrop/whitedrop/blob/feature/v8/scripting/v8/src/Event.cpp But I don't understand modifying signature. Do you mean modifying v8.h directly? – Vinz243 Feb 24 '15 at 20:11
  • No, don't do that :) But for example your code here: Persistent pfn(isolate, args1); callback cb = callback(isolate, pfn); You've typedef'd `callback` to be `Persistent` So that's redundant. Just do `callback cb(isolate, args1);` You're pretty close, templating can be confusing. – justin.m.chase Feb 24 '15 at 21:04
  • Same error cannot convert primitive to volatile :( V8 is just a mess, the signature of each function changes everyday and there is no offcial doc :( – Vinz243 Feb 25 '15 at 10:34
  • Check this out: https://v8docs.nodesource.com/node-0.8/d3/dd5/classv8_1_1_handle.html – justin.m.chase Feb 25 '15 at 15:46
  • I found some unofficial other docs but this site seems best thank you !!! What about my error? – Vinz243 Feb 25 '15 at 16:42
  • The node.js [callbacks example](https://nodejs.org/api/addons.html#addons_callbacks) passes on a `Local<..>` to `Call` - so is it really necessary to cast to `Persistent`? Or is it perhaps only necessary if the called function hangs on to an argument after it is finished? I hope - and would think - not! – Evgeniy Berezovsky Aug 30 '16 at 02:49