C++ scope and Google V8 script context - c++

I have the following, almost working piece of code written in c++:
[..]
Handle<Object> jsGlobal;
Handle<Function> jsUpdateFunc;
void setupJs () {
V8::Initialize();
Isolate* isolate = v8::Isolate::New();
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
Local<String> source = String::NewFromUtf8(isolate, "var a = 0; function test() { a++; return a.toString(); }");
Local<Script> script = Script::Compile(source);
script->Run();
jsGlobal = context->Global();
Handle<Value> value = jsGlobal->Get(String::NewFromUtf8(isolate, "test"));
jsUpdateFunc = Handle<Function>::Cast(value);
}
void callJs() {
Handle<Value> args[0];
Handle<Value> js_result = jsUpdateFunc->Call(jsGlobal, 0, args);
js_result->ToString();
String::Utf8Value utf8(js_result);
printf("%s\n", *utf8);
}
[..]
I have the function setupJs() setup the v8 environment and callJs is supposed to be called multiple times (when working, the javascript script increments var a by one each time).
If i put
Handle<Value> args[0];
Handle<Value> js_result = jsUpdateFunc->Call(jsGlobal, 0, args);
js_result->ToString();
String::Utf8Value utf8(js_result);
printf("%s\n", *utf8);
in setupJs, I can see how the function s called and "1" is printed. But if I leave the function call withing a different function to be called later, i have a Segfault at the line Handle<Value> js_result = jsUpdateFunc->Call(jsGlobal, 0, args);
I've checked and both jsUpdateFunc and jsGlobal are not-null pointers

You need to use persistent handles for jsGlobal and jsUpdateFunc. A normal (local) handle becomes invalid when its enclosing v8::HandleScope is destroyed.
You'll also want a global variable for the v8::Isolate pointer and another one for a persistent handle to the v8::Context.
To call the the script function later, you need to:
Lock the isolate (which you really should do in setupJs as well; see v8::Locker)
Enter the isolate (see v8::Isolate::Scope).
Establish a handle scope (see v8::HandleScope).
Create a local handle for the context.
Enter the context (see v8::Context::Scope).
Create local handles for jsGlobal and jsUpdateFunc.
Call the script function as above.
Look for v8::Persistent and related templates in the V8 header file.

Related

Using a pointer to Isolate in libuv worker thread

I try to develop an async Node.js addon that works with an Isolate.
Consider this example code:
struct Work {
uv_work_t request;
Persistent<Function> callback;
Isolate * isolate;
};
// called in worker thread
static void WorkAsync(uv_work_t *req)
{
Work *work = static_cast<Work *>(req->data);
HeapStatistics stats;
work->isolate->GetHeapStatistics(&stats);
// ... do other stuff ...
}
// called by in main thread
static void WorkAsyncComplete(uv_work_t *req, int status)
{
Isolate * isolate = Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
Work *work = static_cast<Work *>(req->data);
// ... do other stuff ...
work->callback.Reset();
delete work;
}
void RunAsync(const v8::FunctionCallbackInfo<v8::Value>&args) {
Isolate* isolate = args.GetIsolate();
Work * work = new Work();
work->request.data = work;
work->isolate = isolate;
Local<Function> callback = Local<Function>::Cast(args[0]);
work->callback.Reset(isolate, callback);
// kick of the worker thread
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
void setup(Handle <Object> exports, Handle<Object> module) {
NODE_SET_METHOD(exports, "run", RunAsync);
}
NODE_MODULE(addon, setup)
My question: is it safe to pass a pointer to current Isolate to a worker thread (for read-only purposes)?
Thanks!
Answered by Scott Frees (the author of C++ and Node.js Integration ebook: https://scottfrees.com/ebooks/nodecpp/):
To be honest, I've never tried to do that. It seems to me that it should be fine - as long as you are only making calls on the isolate and not actually modifying anything within it.
Generally, if you try to do anything in violation with the V8 threading model, it will generate an exception - so if it's not, then I'd assume it is ok.
Yes it's possible. However, you are the responsable of the safety. You need to be sure that Isolate is leaved from the parent thread and unlocked. Then it can be used (Entred) in the worker thread.

Invoke javascript callback repeatedly from C++ module in node.js

So I am writing a node.js module in C++ which processes data from a camera. I want to invoke a callback function in my app.js file whenever new data is available.
What I have at the moment
Right now I have in my node javascript file (app.js) the following code. Every second it calls a function in my C++-Module and returns the number of frames that have been processed so far:
setInterval(function () {
var counter = MyCPPModule.NumberOfFrames();
console.log(counter);
}, 1000);
In my C++ file I have the following functions.
1.) I have a function to get the number of frames - the function that gets called from javascript as above- , where frameCounter refers to a global variable.
void NumberOfFrames(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Local<Integer> retval = v8::Integer::New(isolate, frameCounter);
args.GetReturnValue().Set(retval);
}
2.) And I have a C++-callback function in the C++ module that gets triggered whenever a new frame is available (just to be clear, this callback is independent of node.js and has nothing directly to do with node.js-callbacks).
void NewFrameAvailable(char** imageBuffer, /* more arguments */)
{
// ...
frameCounter++; // increment global variable
// ...
}
All of this works fine.
What I would like to accomplish
Instead of registering a function with setInterval in my javascript code. I would like to somehow register a function with my C++ module that gets called repeatedly whenever a new frame is available from the camera. It should behave like setInterval, but instead of being triggered every second, it gets triggered when a frame is available.
The code that I am hoping for on the javascript-side would be something like:
MyCPPModule.setMyFrameInterval(function (msg) {
console.log(msg);
});
Inside the C++ Module I would like to do something like:
void NewFrameAvailable(char** imageBuffer, /* more arguments */)
{
// ...
frameCounter++; // increment global variable
Local<Function> cb = /* WHAT DO I DO HERE*/ ;
Isolate*isolate = /* WHAT DO I DO HERE */;
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, std::to_string(frameCounter)) };
cb->Call(Null(isolate), argc, argv);
// ...
}
and a function that registers the javascipt-callback with setMyFrameInterval:
void setMyFrameInterval(const FunctionCallbackInfo<Value>& args) {
Local<Function> cb = Local<Function>::Cast(args[0]); // make this somehow global?
Isolate*isolate = args.GetIsolate()
//...
}
So, how can I invoke the javascript callback from NewFrameAvailable (the C++ callback function which gets triggered when frames are available). I think I basically have to make the javascript function somehow globally available in the setMyFrameInterval function so that it is also known to the newFrameAvailable function. How do I do this?

Integrating third party async libraries with NodeJS

How do I implement Node JS addon with asnyc third part library.
I am able to implement synchronous functions but when it comes to asynchronous functions not sure how exactly it works. Should it be invoked from main loop or it should be in async_work.
The scenario is similar to example explained in http://nikhilm.github.io/uvbook/threads.html#inter-thread-communication but instead of downloading in async_work, I want to invoke asynchronous function which takes care of downloading.
Example async function : download(url, callback);
Sample Code:
Handle<Value> DNS::Download(const Arguments& args) {
HandleScope scope;
uv_loop_t *loop = uv_default_loop();
uv_async_t *uv_async_req = new uv_async_t;
uv_work_t *uv_work_req = new uv_work_t;
AsyncData *asyncData = new AsyncData;
asyncData->url = "http://..../";
uv_async_req->data = asyncData;
uv_work_req->data = asyncData;
uv_async_init(loop, uv_async_req, send_progress);
uv_queue_work(loop, uv_work_req, initiate_download, after);
//Not sure if i have to invoke download(url, callback); here itself
//or in fake_download
return scope.Close(Undefined());
}
void send_progress(uv_async_t *handle, int status /*UNUSED*/) {
AsyncData *asyncData = (AsyncData*)handle->data;
Handle<Value> values[] = {};
//Invoking js callback function.
asyncData->callback->Call(Context::GetCurrent()->Global(), 0, values);
}
void initiate_download(uv_work_t *req) {
//I would like to invoke non blocking async function here
download(url, callback);
}
void callback(status, size) {
//Send event to the send_progress
uv_async_send(&async);
}
My call back is not getting invoked at all in both the cases(Invoking in main thread or in async_work.) and JavaScript keeps waiting for the callback.
Any examples are greatly appreciated.
Thanks in advance.

v8 persistent setWeak callback not getting called

Using Google's v8 C++ library, I have tried creating a v8::UniquePersistent instance on the heap which holds an internal pointer to some memory that was allocated in C++, and called SetWeak() on the Persistent to specify a callback, which would do the necessary cleanup by deallocating the memory that the internal pointer in the Persistent refers to when that handle is no longer needed by the javascript engine. Although the creation of the Persistent seems to work, I am finding that my callback function is never getting invoked.
I am completely stumped here. Any ideas what might I be doing wrong?
The callback is invoked only when the object is garbage collected. Try this example:
void callback(v8::WeakCallbackData<v8::Object, int> data)
{
Local<Object> val = data.GetValue();
int* ptr = reinterpret_cast<int*>(val->GetAlignedPointerFromINternalField(0));
delete ptr;
fprintf(stdout, "Deleted internal object!\n");
}
void main()
{
using namespace v8;
// Enable garbage collection
const char* flags = "--expose_gc";
v8::V8::SetFlagsFromStrings(flags, strlen(flags));
Isolate isolate = Isolate::New();
isolate->Enter();
HandleScope scope(isolate);
Eternal<v8::Context> context;
Local<ObjectTemplate> ctx_template ObjectTemplate::New(isolate);
context.Set(isolate, Context::New(isolate, nullptr, ctx_template);
Local<ObjectTemplate> obj_template = ObjectTemplate::New(isolate);
obj_template->SetInternalFieldCount(1);
Local<Object> obj = Local<Object>(isolate, obj_template->NewInstance());
obj->SetAlignedPointerInInternalField(0, new int);
UniquePersistent<Object> persistent(isolate, obj);
persistent->SetWeak(nullptr, callback);
//...do something with persistent
persistent.Reset();
isolate->RequestGarbageCollectionForTesting(v8::Isolate::kFullGarbageCollection);
// You should see output from the callback
// i need to add more words to pass edit word num check...
}

Calling callback from node.js native code

I'm writing an add-on for node.js using c++.
here some snippets:
class Client : public node::ObjectWrap, public someObjectObserver {
public:
void onAsyncMethodEnds() {
Local<Value> argv[] = { Local<Value>::New(String::New("TheString")) };
this->callback->Call(Context::GetCurrent()->Global(), 1, argv);
}
....
private:
static v8::Handle<v8::Value> BeInitiator(const v8::Arguments& args) {
HandleScope scope;
Client* client = ObjectWrap::Unwrap<Client>(args.This());
client->someObject->asyncMethod(client, NULL);
return scope.Close(Boolean::New(true));
}
static v8::Handle<v8::Value> SetCallback(const v8::Arguments& args) {
HandleScope scope;
Client* client = ObjectWrap::Unwrap<Client>(args.This());
client->callback = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
return scope.Close(Boolean::New(true));
}
I need to save a javascript function as callback to call it later.
The Client class is an observer for another object and the javascript callback should be called from onAsyncMethodEnds.
Unfortunately when I call the function "BeInitiator" I receive "Bus error: 10" error just before the callback Call()
thanks in advice
You cannot ->Call from another thread. JavaScript and Node are single threaded and attempting to call a function from another thread amounts to trying to run two threads of JS at once.
You should either re-work your code to not do that, or you should read up on libuv's threading library. It provides uv_async_send which can be used to trigger callback in the main JS loop from a separate thread.
There are docs here: http://nikhilm.github.io/uvbook/threads.html