4

I would like to call a nodejs callback from within my asynchronous addon function. I have seen the synchronous example (here) and I am using a wonderful asynchronous example (here) as a starting base.

However, when I try to execute a callback that was given to the c++ AsyncWorker child class, I get a Segmentation fault.

Here is my code:

#include <nan.h>
#include <functional>
#include <iostream>
#include <exception>
using namespace Nan;
using namespace v8;
using namespace std;

class ScriptWorker : public AsyncWorker {
  public:
    ScriptWorker(Callback *callback, const std::map<std::string, Callback*>)
    : AsyncWorker(callback), script(script), cbs(cbs) {}

    ~ScriptWorker() {}

    void Execute () {

      // ------------------------
      // Segmentation fault after
      // ------------------------

      Local<Value> argv[] = {
        New<v8::Number>(id)
      };

      // -------------------------
      // Segmentation fault before
      // -------------------------

      cbs["getUser"]->Call(1, argv);
    }

  private:
    std::string script;
    std::map<std::string, Callback*> cbs;
};

NAN_METHOD(Method) {
  Local<Object> array = info[0]->ToObject();
  Callback *callback = new Callback(info[1].As<Function>());

  // Build up callbacks passed in from javascript.
  // Will be a dynamic loop, but for now, hard code the one getUser example.
  std::map<std::string, Callback*> cbs;
  cbs.insert(std::pair<std::string, Callback*>("getUser",
    new Callback(
      array->Get(
        v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), "getUser")
      ).As<Function>()
    )
  ));
  AsyncQueueWorker(new ScriptWorker(callback, cbs));
}

NAN_MODULE_INIT(Init) {
  Nan::Set(target, Nan::New<String>("hello").ToLocalChecked(), Nan::GetFunction(Nan::New<FunctionTemplate>(Method)).ToLocalChecked());
}

NODE_MODULE(hello, Init)

My questions:

  1. Should I not use Nan's AsyncWorker and instead roll my own?
  2. How do I setup the Execute function to call into Javascript?
ZachB
  • 13,051
  • 4
  • 61
  • 89
EToreo
  • 2,936
  • 4
  • 30
  • 36

1 Answers1

5

EDIT:

See this repo:

https://github.com/xavero/node_addon_sample

it has a sample on how to work with callback functions and emitting events from C land.

You should not call v8/Nan functions in your Execute method of ScriptWorker, or you will get segment faults. Override the HandleOKCallback function to use the javascript callback.

To call from the javascript, in your c++ addon:

NAN_MODULE_INIT(Init) {
  Nan::Set(target, Nan::New("myJsFunctionName").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(Method)).ToLocalChecked());
}

NODE_MODULE(anyNameHere, Init)

And in your javascript:

// run "npm install bindings --save" in console first
var addon = require('bindings')('NativeExtension');

addon.myJsFunctionName({ foo: "bar"}, (arg1,arg2) => console.log(`${arg1} - ${arg2}`))
Xavero
  • 3,333
  • 1
  • 15
  • 17
  • 1
    Sorry, I have been on vacation, but now I'm back and ready to get this solved. @Xavero, I forgot to include the NAN_MODULE_INIT and NODE_MODULE calls in my example. They are there now. I see what you are saying about making V8 objects and calls outside the main thread. I think I need to go old-school and use uv_thread_create() and a uv_async_t coupled with a uv_mutex_t to make "custom" (outside of what NAN AsyncWorker expects) calls. Any thoughts? – EToreo Apr 26 '16 at 18:35
  • If you want a callback ( execute a C async function and then a javascript with the response), the right way is with AsyncQueueWorker + ScriptWorker. Trust me. Now, if you want emit a event from C land, then you will need use uv lib – Xavero Apr 27 '16 at 19:48
  • I did this in a project last week... let me setup a gist or something with a simple sample. Give me a few hours. – Xavero Apr 27 '16 at 19:50
  • yeah, i was already thinking in doing this, because i had a hard time to find how to emit events from C to js. I updated the response above with the link. Take a look. Just, don't blindly copy and paste, because i am not a C programmer, so maybe it has some memory leaking. Also, be sure to check the Nan repo. It has great examples and docs. https://github.com/nodejs/nan – Xavero Apr 28 '16 at 13:01
  • Xavero, thank you. This has gotten me over the first hump in my project. – EToreo May 02 '16 at 17:30
  • @Xavero So, I was trying to do it and I'm founding different reasons to believe that it's not posible. Is it because you can't access v8 stuff from a different thread? – betomoretti Nov 21 '17 at 12:42
  • 1
    You can only access v8 stuff inside the Node thread, thats correct. When in another thread, you should sync with Node Thread using libuv, or using AsyncWorker when dealing with callbacks. – Xavero Nov 21 '17 at 17:00
  • I hope you could help me with [a related question](https://stackoverflow.com/q/52056854/2674222) which doesn't involve `AsyncWorker`. – avo Aug 28 '18 at 11:40
  • 1
    The repo linked in the answer has a example, but that its like 2 years old now... new Node Native api may have a better way now. This its the line that starts a thread that sends a message to js, wtihout AsynWoker https://github.com/xavero/node_addon_sample/blob/7907e61b23d573e07e5210e790c2c292233a718b/eventEmitter.cpp#L131 – Xavero Aug 30 '18 at 12:33
  • @Xavero thank you that got me going for now. I've put a small bounty on [my linked question](https://stackoverflow.com/q/52056854/2674222) in case you'd like to post a definitive answer there. – avo Sep 11 '18 at 11:09