Skip to content

How to get callbacks from native cpp code. #1171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jay11ca39 opened this issue Mar 18, 2018 · 30 comments
Closed

How to get callbacks from native cpp code. #1171

jay11ca39 opened this issue Mar 18, 2018 · 30 comments

Comments

@jay11ca39
Copy link

I am new to Native addon concept. I am using NAN to build my native addon.

Following are my layers:

1. C++ shared library
2. Native addon layer [NAN layer]
3. Javascript APIs
4. Javascript Application

I have some query regarding callback mechanism:

How my addon layer will get the callbacks from Native layer [C++ library].
Do i have to use libuv to get callbacks from c++ layer or there are other ways to do so?

Thanks in advance.

@gireeshpunathil
Copy link
Member

what is the relation between your NAN layer and the shared library layer? Does the addon consume some services from the library? then there is no libuv support is required, you invoke the library to avail the functions in the normal manner. On the other hand, if the library is a consumer of libuv and receives event callbacks already and if you want to relay those callback into the addon, the shlib should provide interfaces (event registration functions), addon should register with them, and then the callback from the shlib can pipe them through the callback hooks.

If you have further questions, please elaborate with a specific case, probably with sample code to show what you are trying to achieve.

@jay11ca39
Copy link
Author

Hi @gireeshpunathil ,

Thanks for your reply.

My CPP library is PUB/SUB library.
For example:

  1. One publisher : will push data.
  2. One subscriber : will subscribe for data.

So, CPP subscriber side will recieve data asynchronously. [Whenever publisher publish]
Addon Layer will register the callback to native layer to receive data as and when native layer receive data.
Addon layer will send wrapped data that it received in callback to js layer [application]

Please let me know if it makes sense?

@gireeshpunathil
Copy link
Member

@jay11ca39 - thanks. So, you need 2 things, if I understand it right:

  • ability to get called back from pub/sub lib into the native addon
  • ability to call JS code from native addon.

Please confirm if this is what you wanted.

as pub/sub is already capable of asynchronous data handling, libuv is not required. So all what you need is a mechanism for the native addon to register for being called back. Normal C/C++ semantics, no JS / node specific:

  1. define a function as native addon (say foo)
  2. define a function pointer in the pub/sub library of comparable type with foo
  3. define a registration method in pub/sub lib - that takes a function pointer as argument. Additional input can be defined based on predicates and conditions you want to apply.
  4. In the registration function, cache the incoming fn. pointer.
  5. Upon intercepting async data, and applying the predicates as relevant, invoke the callback through the cached pointer.
  6. On the native addon side, invoke the registration function of the library with the addon as the input.

this setup will provide calling back into the addon. Now to go to JS from the addon, use one of the appropriate Call family of methods from nan.

Does this sound reasonable to you?

@jay11ca39
Copy link
Author

Hi @gireeshpunathil ,

Yes, I want the same:

  • ability to get called back from pub/sub lib into the native addon
  • ability to call JS code from native addon.

Following are my layers:

  1. C++ shared library
  2. Native addon layer
  3. Javascript APIs
  4. Javascript Application

What i am trying:

Native C++ class

typedef std::function<void(int errorCode)> nativeCallback;
Class NativeClass 
{
  public:
       NativeClass() {}   
       setCallback(nativeCallback cb) //some function of this clas will call this callback later...
       ....................

};

Addon class

Class AddonClass
{
  public:
       AddonClass() {}   
       static addonCallback(AddonClass *pointer) {} 
       ....................

};

I tried the follwing:
Define a static function in addon layer and pass it to the native cpp layer.
Result:
I am getting the native callback in my addon layer. But when i am trying to call it to javascript using :

void AddonClass::addonCallback(AddonClass *pointer) //This object is maintained using std::bind
{

        std::cout <<"[Addon addonCallback] Before getting handle"<<std::endl;
        pointer->handle();   --------------------->   **//This is crashing** 
        std::cout <<"[Addon addonCallback] After getting handle"<<std::endl;
       v8::Local<v8::Value> argv[] = { Nan::New("onEvent").ToLocalChecked() };
       pointer->emit->Call(pointer->handle(), 1, argv);

}

When i added Nan::HandleScope scope before getting the handle it is also crashing.

Note: I am trying to use event emmiter concept for giving the callbacks to javascript layer.

@gireeshpunathil
Copy link
Member

@jay11ca39 - there are at least 3 ways to invoke a callback, at high level:

  1. If you statically know the target method who is responsible for handling the event, invoke it directly with the argument data.
  2. If you don't know the target method, but know the receiver object upon which the event has occurred, invoke it's emit method with the argument data.
  3. In both the cases above, if your calling in is synchronous (i.e., the sequence of instructions that lead upto invocation of the original async function and the current callback context is in the same control flow without any other unrelated code ran in between) then you may want to mimic an async behavior through skipping the current execution cycle and call the callback in the next one - this requires libuv support (process.nextTick ...)

Assuming you are interested in (1), here is the sample code. You can extend it to (2) or (3) as you like:

#cat foo.cc

#include <nan.h>

Nan::MaybeLocal<v8::Function> fn;
v8::Local<v8::Value> argv[] {Nan::Null()};
Nan::Callback cb;

NAN_METHOD(Register) {
  const v8::Local<v8::Function> val = Nan::To<v8::Function>(info[0]).ToLocalChecked();
  cb.Reset(val);
}

NAN_METHOD(Invoke) {
  cb.Call(1, argv);
}

NAN_MODULE_INIT(Init) {
Nan::Set(target
    , Nan::New<v8::String>("register").ToLocalChecked()
    , Nan::New<v8::FunctionTemplate>(Register)->GetFunction()
  );
Nan::Set(target
    , Nan::New<v8::String>("invoke").ToLocalChecked()
    , Nan::New<v8::FunctionTemplate>(Invoke)->GetFunction()
  );
}

NODE_MODULE(addon, Init)

#cat binding.gyp

{
  "targets": [
    {
    "xcode_settings": {
     "OTHER_CPLUSPLUSFLAGS": [ "'-I../../../node_modules/nan'"],
    },
      "target_name": "testmodule",
      "sources": [ "foo.cc" ]
    }
  ]
}

#cat foo.js

const tm = require('./testmodule');
function foo() {
  console.log('hey, I am a callback!')
  console.trace();
}
tm.register(foo);
tm.invoke();

#node-gyp configure build V=1

gyp info it worked if it ends with ok
gyp info using node-gyp@3.6.2
gyp info using node@9.8.0 | darwin | x64
gyp info spawn /usr/local/bin/python2
...
gyp info ok 

#cp build/Release/testmodule.node ./testmodule.node
#node foo.js

hey, I am a callback!
Trace
    at foo (/home/gireesh/support/1171/foo.js:4:11)
    at Object.<anonymous> (/home/gireesh/support/1171/foo.js:7:4)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Function.Module.runMain (module.js:690:10)
    at startup (bootstrap_node.js:194:16)
    at bootstrap_node.js:666:3
#

Hope this helps!

@jay11ca39
Copy link
Author

@gireeshpunathil ,
Thank you very much for your detailed explaination.
I understood and run your sample it worked well.

But when i modified your approch to achieve my scenario it crashed while calling the javascript callback.
Following is the code I tried:

You are calling javascript callback from here:

NAN_METHOD(Invoke) {
  cb.Call(1, argv);
}

I have to call this callback from addon callback which i will register to cpp library :

Class AddonClass
{
  public:
       AddonClass() {}   
       static addonCallback() //This will be called from C++ library
      {   
          cb.Call(1, argv);   //This result in crash
      } 
};

So, does it mean i can call javascript callback only from NAN_METHOD and not from my addon class other methods?

Is there is anything I missed or there is some other mechanism I have to use...

@jay11ca39
Copy link
Author

jay11ca39 commented Mar 22, 2018

Sorry for asking many things.
I want to send other addon class object rather than primitive types in NAN::Callback . Is it possible?
I went through the documentation of NAN for this but could not get much on this.
NAN::Callback

As per documentation:

  v8::Local<v8::Value> Call(v8::Local<v8::Object> target,
                            int argc,
                            v8::Local<v8::Value> argv[]) const;

  v8::Local<v8::Value> Call(int argc, v8::Local<v8::Value> argv[]) const;

I am trying to achieve this:

Class AddonA
{
     // here in the callback of class A I want to send object of Class B with value member = 100 to js layer
};

Class B
{
  public: 
    int value;
};

@gireeshpunathil
Copy link
Member

no, there exist no such restrictions. The crash reason probably is your cb is not properly registered or initialized within your AddonClass. Mind sharing the whole AddonClass code?

@gireeshpunathil
Copy link
Member

you can send user defined objects through NAN:Callback, but not C++ objects! As the NAN::Callback is destined to JS land, your objects have to be JS objects! sounds reasonable?

@jay11ca39
Copy link
Author

Hello @gireeshpunathil ,
Thanks for your reply.

Following is my addon class:

NSubscriber.h

#ifndef N_SUBSCRIBER_H
#define N_SUBSCRIBER_H

#include <nan.h>

#include "NSubscriber.h"

using namespace Nan;
using namespace v8;

namespace npubsub
{
    class NSubscriber : public Nan::ObjectWrap
    {
        public:
            static NAN_MODULE_INIT(Init);
            static v8::Local<v8::Object> NewInstance();

            //callback functions
            static void subCallback(const Message &event, NSubscriber *obj);

        private:
            explicit NSubscriber();
            void NMakeNative(std::string address);

            static NAN_METHOD(New);
            static NAN_METHOD(Subscribe);

            static inline Nan::Persistent<v8::Function> & constructor()
            {
                static Nan::Persistent<v8::Function> constructor;
                return constructor;
            }

            pubsub::Subscriber *nativeHandle;
    };
}
#endif //N_SUBSCRIBER_H

NSubscriber.cpp

#include "NSubscriber.h"

using namespace v8;

namespace npubsub
{
    Nan::MaybeLocal<v8::Function> fn;
    v8::Local<v8::Value> argv[] {Nan::Null()};
    Nan::Callback gcb;

    NAN_MODULE_INIT(NSubscriber::Init)
    {
        // Prepare constructor template
        v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
        tpl->SetClassName(Nan::New("NSubscriber").ToLocalChecked());
        tpl->InstanceTemplate()->SetInternalFieldCount(1);

        // Function Prototypes
        Nan::SetPrototypeMethod(tpl, "subscribe", Subscribe);

        constructor().Reset(GetFunction(tpl).ToLocalChecked());
        Nan::Set(target, Nan::New("NSubscriber").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
    }

    v8::Local<v8::Object> NSubscriber::NewInstance()
    {
        v8::Local<v8::Function> cons = Nan::New(constructor());
        return Nan::NewInstance(cons).ToLocalChecked();
    }

    NSubscriber::NSubscriber() : Nan::ObjectWrap()
    {	

    }
	
    void NSubscriber::NMakeNative(std::string address)
    {
       //make a cpp object and store it in nativeHandle
	nativeHandle =  new pubsub::Subscriber(address, std::bind(NSubscriber::subCallback, std::placeholders::_1, this));	
     }
	
    //The below method is being called from native layer properly....
    void NSubscriber::subCallback(const EZMQMessage &message,  NSubscriber *obj)
    {
       std::cout <<"[Addon subCallback] Received data from CPP layer"<<std::endl;
       nezmq::gcb.Call(1, nezmq::argv);  // ***************** Here it is crashing 
	  
	// one more question here:
	// 1. Suppose I have one more class in addon layer that is NMessage and I want to send object
	// of that class in above callback, how can I do that?  
    }

    NAN_METHOD(NSubscriber::New)
    {
        Isolate* isolate = info.GetIsolate();
        if (info.IsConstructCall())
        {
            //Get address
            Local<String> str = Local<String>::Cast(info[0]);
            String::Utf8Value utfValue(str);

            NSubscriber* obj = new NSubscriber();
            obj->NEZMQMakeNative(*utfValue);

            //Get Callback
            const v8::Local<v8::Function> val = Nan::To<v8::Function>(info[2]).ToLocalChecked();
            gcb.Reset(val);

            obj->Wrap(info.This());
            info.GetReturnValue().Set(info.This());
        }
        else
        {
        }
    }
	
    NAN_METHOD(NSubscriber::Subscribe)
    {
       NSubscriber* obj = ObjectWrap::Unwrap<NSubscriber>(info.Holder());
       info.GetReturnValue().Set(Nan::New<Integer>((obj->nativeHandle)->subscribe()));
    }
}

@jay11ca39
Copy link
Author

jay11ca39 commented Mar 22, 2018

When I just tried to call the js callback from subscribe method itself it is working well..

like this:

 NAN_METHOD(NSubscriber::Subscribe)
    {
       NSubscriber* obj = ObjectWrap::Unwrap<NSubscriber>(info.Holder());
       info.GetReturnValue().Set(Nan::New<Integer>((obj->nativeHandle)->subscribe()));

         npubsub::gcb.Call(1, npubsub::argv);   //It is working fine .. I am able to get callback to js application.
    }

The same thing is crashing when i called from my addon callback [subCallback] which will be called from c++ layer..

@gireeshpunathil
Copy link
Member

can you paste here the call stack from the crash?

@jay11ca39
Copy link
Author

jay11ca39 commented Mar 22, 2018

Hello @gireeshpunathil

Following is the stack trace:

(gdb) bt
#0  0x0000000000aeb4a3 in v8::EscapableHandleScope::EscapableHandleScope(v8::Isolate*) ()
#1  0x00007ffff49ab185 in npubsub::NSubscriber::subCallback(pubsub::pubsubMessage const&, npubsub::NSubscriber*) ()
   from /home/jay/nodesdk/pubsub-node/build/Release/pubsub.node
#2  0x00007ffff4757a5d in std::function<void (pubsub::pubsubMessage const&)>::operator()(pubsub::pubsubMessage const&) const (this=0x2257e28,
    __args#0=...) at /usr/include/c++/4.8/functional:2471
#3  0x00007ffff4752549 in pubsub::Subscriber::parseSocketData (this=0x2257e00) at src/Subscriber.cpp:138
#4  0x00007ffff4752fe7 in pubsub::Subscriber::receive (this=0x2257e00) at src/Subscriber.cpp:180
#5  0x00007ffff475bbb5 in std::_Mem_fn<void (pubsub::Subscriber::*)()>::operator()<, void>(pubsub::Subscriber*) const (this=0x2282598,
    __object=0x2257e00) at /usr/include/c++/4.8/functional:601
#6  0x00007ffff475ba87 in std::_Bind_simple<std::_Mem_fn<void (pubsub::Subscriber::*)()> (pubsub::Subscriber*)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0x2282590) at /usr/include/c++/4.8/functional:1732
#7  0x00007ffff475b82f in std::_Bind_simple<std::_Mem_fn<void (pubsub::Subscriber::*)()> (pubsub::Subscriber*)>::operator()() (
    this=0x2282590) at /usr/include/c++/4.8/functional:1720
#8  0x00007ffff475b75c in std::thread::_Impl<std::_Bind_simple<std::_Mem_fn<void (pubsub::Subscriber::*)()> (pubsub::Subscriber*)> >::_M_run() (this=0x2282578) at /usr/include/c++/4.8/thread:115
#9  0x00007ffff774c5b0 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#10 0x00007ffff6f88184 in start_thread (arg=0x7fffe6de4700) at pthread_create.c:312
#11 0x00007ffff6cb503d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

@gireeshpunathil
Copy link
Member

  • is gcb part of NSubscriber class? what is nezmq in nezmq::gcb?
  • the crash reason is due to lack of scoping . Can you please review and apply this nan doc - specifically:
    • Allocate a new Nan::HandleScope whenever you are creating new V8 JavaScript objects.
    • Nan::EscapableHandleScope: Similar to Nan::HandleScope but should be used in cases where a function needs to return a V8 JavaScript type that has been created within it.

@jay11ca39
Copy link
Author

jay11ca39 commented Mar 22, 2018

Hello @gireeshpunathil

is gcb part of NSubscriber class? what is nezmq in nezmq::gcb?

  • I was testing some other thing and pasted wrongly in above comment:
    Below is the code that i meant:
 NAN_METHOD(NSubscriber::Subscribe)
    {
       NSubscriber* obj = ObjectWrap::Unwrap<NSubscriber>(info.Holder());
       info.GetReturnValue().Set(Nan::New<Integer>((obj->nativeHandle)->subscribe()));

         npubsub::gcb.Call(1, npubsub::argv);   //It is working fine .. I am able to get callback to js application.
    }

I will try to apply.. so as per you if i can make use of scoping in my addon callback to call js callback right?

@gireeshpunathil
Copy link
Member

right - yes.

In addition, also check what (methods) in NSubscriber need to be static and what needs to be non-static etc. That also can be a source of trouble, later.

@jay11ca39
Copy link
Author

Hello @gireeshpunathil ,

I applied the changes for scope but it is still crashing. I tried to apply both types of scope handle before acccessing V8 object. But it did not help.

I raised the isse in NAN community as well : NAN github issue

There it is suggested not use V8 objects in addon class callback. I am little confused .:(

@gireeshpunathil
Copy link
Member

the suggestion from that thread is not to use v8 objects in a SECOND thread. The basic assumption is that your C++ library routines are executed in the same thread as the JS /addon thread, isn't that the case

@jay11ca39
Copy link
Author

Hello @gireeshpunathil ,

when cpp library is started by application, it will start a thread internally which will be running continously and when it get any data it will call the application callback.. So, addon thread and cpp library thread are different..
So, what sould I use.

@gireeshpunathil
Copy link
Member

looking back at the stack trace (btw I see you have edited the stack trace and changed the frames! - request not to do that, as it mess up the history and confuses reviewers) , it is evident that you are in another thread. Crash reason is evident, as the v8 object heap is not designed for multi-thread concurrent access.

@gireeshpunathil
Copy link
Member

ok, thanks for the clarification, let me see what can be done. stay tuned.

@gireeshpunathil
Copy link
Member

#include "uv.h"

static uv_async_t ah;
static uv_thread_t thread;

static void tcb(void* dummy) {
  fprintf(stderr, "called in the second thread: 0x%x\n", pthread_self());
  uv_async_send(&ah);
}

static void acb(uv_async_t* handle) {
  fprintf(stderr, "called back in the main thread: 0x%x\n", pthread_self());
  uv_unref((uv_handle_t*)handle);
}

int main() {
  uv_async_init(uv_default_loop(), &ah, acb);
  uv_thread_create(&thread, tcb, NULL);
  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
  uv_thread_join(&thread);
  return 0;
}
#./a.out
called in the second thread: 0xcf0c000
called back in the main thread: 0xdf25e3c0

In this code, main and acb run on the main thread, and tcb runs on a second thread. You should follow portion of code in main to setup async_init and bind it with a callback. The second thread should have access to the async_id (ah) thus created. It, at appropriate times, invokes uv_async_send to signal main thread. Main thread then performs actions in response to that.

Hope it helps!

@jay11ca39
Copy link
Author

jay11ca39 commented Mar 23, 2018

@gireeshpunathil ,
One more query:
https://github.com/nodejs/nan/blob/master/doc/asyncworker.md is different thing and for my scenario I have to go with libuv right?

I am also checking and in case you have any knowledge about about libuv, will it create new threads when i call uv_async_init. As In my case there many be many subscriber so if I have one uv instance per subscriber it will be bottleneck...

@gireeshpunathil
Copy link
Member

my understanding is that AsyncWorker uses AsyncQueueWorker which leverages pre-created internal threads in the libuv threadpool , so it may be lot of work for your use case - as your code involves a pre-existing thread.

uv_async_init does not involve new threads, it binds the loop with an asyncID and the callback that needs to be called in response to the async event. Once you inited, your pre-existing thread is free to use it to post signal to the main thread, through uv_async_send.

I just tested with your code using the above approach, and it is working fine.

@jay11ca39
Copy link
Author

Hello @gireeshpunathil ,

I have tried with libuv approach, as you shown in example.
With that I am able to get callback from addon layer to js layer.. Cheers.. !!!!!!

  1. Is it possible to send the message that i received in NSubscriber::subCallback to NSubscriber::acb, so that i can call js callback with that data from there.

    • Currently I am acheiving this using global variables in my addon subscriber class..:(
  2. As per the documentation of : http://docs.libuv.org/en/v1.x/async.html#c.uv_async_t

Warning libuv will coalesce calls to uv_async_send(), that is, not every call to it will yield an execution of the callback. For example: if uv_async_send() is called 5 times in a row before the callback is called, the callback will only be called once. If uv_async_send() is called again after the callback was called, it will be called again.

So it can drop some of my native callback. :(
-> may be some mutex mechanism i can use to handle this right?

@gireeshpunathil
Copy link
Member

@jay11ca39 - great to know you were able to get the callback invoked!

  1. you can send data through data field of the async_handle,whose type is generic pointer (void *) so you can attach anything to it. The code below demonstrates that.

  2. The coalescence is relevant on a per async handle basic. Two or more different handles do not get merged into one. This is also demonstrated in the below code. As the purpose of async_send is to notify the main thread, back-to-back signalling being converted to one makes sense. However, if your library thread is streaming data (highly intense, unidirectional data flow) then the sender and the receiver need to co-ordinate so that the sent data is not lost.

Hope this helps!

#include "uv.h"
#include <stdlib.h>

static uv_async_t ah1, ah2;
static uv_thread_t thread1, thread2;
static int sync = 1;

static void tcb1(void* dummy) {
  char *data = (char *) malloc(128);
  strcpy(data, "tcb1 custom data!");  
  ah1.data = data;
  fprintf(stderr, "thread: %p\n", pthread_self());
  uv_async_send(&ah1);
}
static void tcb2(void* dummy) {
  fprintf(stderr, "thread: %p\n", pthread_self());
  uv_async_send(&ah2);
  uv_async_send(&ah2);
  uv_async_send(&ah2);
  uv_async_send(&ah2);
  uv_async_send(&ah2);
  uv_async_send(&ah2);
  uv_async_send(&ah2);
  uv_async_send(&ah2);
}

static void acb1(uv_async_t* handle) {
  fprintf(stderr, "acb1 in main thread: %p\n", pthread_self());
  fprintf(stderr, "thread data: %s\n", (char *) handle->data);
  free(handle->data);
  uv_unref((uv_handle_t*)handle);
}
static void acb2(uv_async_t* handle) {
  fprintf(stderr, "acb2 in main thread: %p\n", pthread_self());
  fprintf(stderr, "thread data: %s\n", (char *) handle->data);
  sync = 1;
  free(handle->data);
  uv_unref((uv_handle_t*)handle);
}

int main() {
  uv_async_init(uv_default_loop(), &ah1, acb1);
  uv_async_init(uv_default_loop(), &ah2, acb2);
  uv_thread_create(&thread1, tcb1, NULL);
  uv_thread_create(&thread2, tcb2, NULL);
  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
  uv_thread_join(&thread1);
  uv_thread_join(&thread2);
  return 0;
}

./a.out

thread: 0x70000963d000
thread: 0x700008e3a000
acb1 in main thread: 0x7fffc434a3c0
thread data: tcb1 custom data!
acb2 in main thread: 0x7fffc434a3c0
thread data: (null)
#

@jay11ca39
Copy link
Author

Hello @gireeshpunathil ,
I am able to pass data from native thread to event loop thread. and able to call JS callback with data.
Thanks for your help and support.. :)

I have one last query:
In my scenario, there can be multiple subscribers:

  1. Addon Layer: Different addon subscriber objects will be there and each one will make use of :

    • uv_async_init(uv_default_loop(), &uvAsyncHandle, NSubscriber::uvAsynCallback)
    • uv_async_send(uvAsyncHandle)
      I am having this uvAsyncHandle as my addon class member.
  2. C++ library:
    For each subscriber currently C++ library is creating different thread for recieving data.

I am using mutex lock mechanism i.e. once I get callback in my native addon callback I will call uv_async_send(uvAsyncHandle) and I will lock till js callback is called.

My doubt is about UV library: will it be internally synchronized throughout the process. I mean in case of many native threads can send callbacks to different subscriber in the addon layer and from there it has to call uv_async_send() to send it to js thread. will it be done one at a time throughout the process.

@gireeshpunathil
Copy link
Member

  • you don't have to synchronize anything between addon thread and the js thread | uv thread | main thread. the uv_async_send takes care of that co-ordination.
  • however, if many threads are calling into the addon function that make use of shared / un-shared data, you need to make sure your addon method is re-entrant / thread-safe within its on operating space.
  • if multiple threads are sending data to the JS layer, consider using async id per thread - that is, one id per addon object. This reduces the overhead of synchronization within the addon. That is, consider asyncID as an instance field (object) as opposed to class field (static). You can however, attach the same async_callback with all the asyncIDs.
  • this also makes sure that the JS callbacks are not coalesced when parallel async_sends are issued.
  • however, if same thread issues back-to-back async_sends , the addon should make sure to co-ordinate that (as mentioned earlier), to make sure you get all the data, even though the callbacks are coalesced.

Hope this helps.

@gireeshpunathil
Copy link
Member

@jay11ca39 - is your problem resolved? Is there anything outstanding on this thread?

@jay11ca39
Copy link
Author

Thanks @gireeshpunathil .. The issue is being resolved. Thank you very much for your help and support.
This issue can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants