Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I store a reference to a function so I can call it back later in a node.js C++ addon module?

Here is a node.js addon module I've written in C++ and built using node-gyp. When StoreFunction I am trying to store a pointer to the function so I can use it later

When I try to invoke it later though in InvokeFunction I get a Segmentation fault. What baffled me if I examined the pointer in both functions (using cout) they are the same value.

So I'm guessing either the change of invoking context changes between calling the two functions or I don't understand what I'm pointing to.

All (ummmmmm) pointers gratefully received on my problem here..............

#include <node.h>
#include <v8.h>

using namespace v8;
v8::Persistent<v8::Function> callbackFunction;
 Handle<Value> StoreFunction(const Arguments& args) {
    HandleScope scope;
    callbackFunction = *Local<Function>::Cast(args[0]);
    return scope.Close(Undefined());
}

Handle<Value> InvokeFunction(const Arguments& args) {
    HandleScope scope;
    Local<Value> argv[1] = { String::New("Callback from InvokeFunction")};
    callbackFunction->Call(Context::GetCurrent()->Global(), 1, argv);
    return scope.Close(Undefined());
}

void init(Handle<Object> target) {
  NODE_SET_METHOD(target, "StoreFunction", StoreFunction);
  NODE_SET_METHOD(target, "InvokeFunction", InvokeFunction);
}

NODE_MODULE(someaddonmodule, init);

And of course some calling js...........

var myaddon = require('../build/Release/someaddonmodule');
myaddon.StoreFunction(function(data){   
    console.log("Called back: "+data);
});

myaddon.InvokeFunction();   //causes a segmentation fault
like image 606
Darren White Avatar asked Oct 21 '22 14:10

Darren White


1 Answers

The answer is because we're not programming in Java any more Toto. The pointer I created is pointing at the Local Handle, rather than the function. Holding a 'reference' to this isn't enough to stop the V8 garbage collection destroying it when the scope closes.

To deal with this an explicit request needs to be made to V8 to put aside some memory to hold the function which done like this :

Persistent< Function > percy;
Local<Function> callbackFunction = Local<Function>::Cast(args[0]);
percy = Persistent<Function>::New(callbackFunction);

If anyone with a better understanding of V8 internals knows more than this, I'd still really like to hear your explanation :)

like image 161
Darren White Avatar answered Oct 23 '22 05:10

Darren White