Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callback NodeJS Javascript function from multithreaded C++ addon

I have a multithreaded C++ addon that does some background processing and I need to have it periodically callback to a Javascript function that I wrote in my NodeJS server.

I understand that this involves using uv_async_send(), since it needs to get executed in the main thread, but thus far I haven't been able to figure out how to do it.

Is there a simple example out there that I've missed?

like image 286
logidelic Avatar asked May 02 '16 16:05

logidelic


1 Answers

Finally, this was not too difficult once I understood what the the uv_* functions do:

1) Expose a function in the addon to allow Node to set the Javascript cb that will be periodically called back to:

Callback* cbPeriodic; // keep cbPeriodic somewhere
NAN_METHOD(setPeriodicCb) {
    cbPeriodic = new Callback(info[0].As<Function>());
    //...
}

2) Init UV with an uv_async_t instance and a function that will be executed in the main thread by UV when our worker thread calls uv_async_send()

uv_async_t async; // keep this instance around for as long as we might need to do the periodic callback
uv_loop_t* loop = uv_default_loop();
uv_async_init(loop, &async, asyncmsg);

void asyncmsg(uv_async_t* handle) {
  // Called by UV in main thread after our worker thread calls uv_async_send()
  //    I.e. it's safe to callback to the CB we defined in node!
  Nan::HandleScope scope;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  Local<Value> argv[] = { v8::String::NewFromUtf8(isolate, "Hello world") };
  cbPeriodic->Call(1, argv);
}

3) Call uv_async_send from a worker thread, passing our async instance above, whenever we need to do the periodic callback

uv_async_send(&async);

4) Finally, when we no longer need to execute the callback ever again, clean things up:

uv_close((uv_handle_t*) &async, NULL);

Addendum:

Since I wrote this answer I've run into other issues and finally learned some lessons about libuv. For completeness, you should understand:

  • Aside from uv_async_send, all libuv functions may only be called from the the main loop thread! (I had seen it mentioned that the others were not thread-safe, but this is too weak of a statement.) Even, for example, uv_async_init and uv_close must be called from the main loop thread.

  • If your uv_async_t instance is dynamically allocated, note that you may not free up memory until uv_close makes its callback to let you know that it is safe to do so.

I.e.:

auto async = new uv_async_t();
...
uv_close((uv_handle_t*)async, [](uv_handle_t* handle) {
    delete handle;
});
like image 124
logidelic Avatar answered Sep 25 '22 15:09

logidelic