Luis Crespo Luis Crespo - 2 months ago 19
C++ Question

Node.js and C/C++ integration: how to properly implement callbacks?

I am trying to implement a C++ extension to be integrated with node.js. This extension will internally invoke some blocking calls, so it needs to provide a non-blocking interface to the node.js world.

As specified in https://nodejs.org/api/addons.html, there are two ways to implement non-blocking callbacks:

a) By using a simple callback to a JavaScript function. So my extension would have to spawn a thread and return immediately, and let that thread call the blocking code and then invoke the JavaScript callback upon return. This seems relatively simple to implement.

b) By using the libuv library in order to, if I understood correctly, post an event to the node.js event loop. I have not read libuv documentation in detail, but this seems quite complex to implement.

My preference of course is a), but I have no idea of what are the implications. Is there any problem if the callback is invoked from a different thread, thus cimcurventing the node.js standard approach to non-blocking IO? Or does libuv need to be used to properly handle the threading for my code and its blocking calls?

Thank you very much for your help.

Answer

Calling callback from a different thread is not an options, v8 doesn't allow that. So you have to go with b. It's not so hard to implement actually. I recommend to use nan for this task. Here is an example from docs:

class PiWorker : public NanAsyncWorker {
 public:
  PiWorker(NanCallback *callback, int points)
    : NanAsyncWorker(callback), points(points) {}
  ~PiWorker() {}

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute () {
    estimate = Estimate(points);
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback () {
    NanScope();

    Local<Value> argv[] = {
        NanNull()
      , NanNew<Number>(estimate)
    };

    callback->Call(2, argv);
  };

 private:
  int points;
  double estimate;
};

// Asynchronous access to the `Estimate()` function
NAN_METHOD(CalculateAsync) {
  NanScope();

  int points = args[0]->Uint32Value();
  NanCallback *callback = new NanCallback(args[1].As<Function>());

  NanAsyncQueueWorker(new PiWorker(callback, points));
  NanReturnUndefined();
}

Under the hood it will handle calling your code using libuv thread pool and calling callback on the main thread.

Comments