7

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?

1 Answer 1

20

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;
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.