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?
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;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With