1

I need a way to invoke JS callbacks from a C library that uses contexts.

Here's an example:

const ctx1 = mylib_init();
mylib_set_event_callback(ctx1, () => {
  console.log("EVENT");
});
Napi::FunctionReference cb;
bool done = false; // Used to prevent crash on multithreading.
// TSFN would obviously be used; this is just to shorten it.

extern "C" void onEvent(mylib_t* handle, void* userdata) {
  if (cb != nullptr && !done) {
    done = true;
    cb.Call({});
  }
}

Napi::Value MyWrapper::SetEventCallback(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  Napi::Object global = env.Global();

  // info[0] = mylib_t* previously converted to a BigInt.
  // info[1] = JS callback
  mylib_t* handle = convertJSBigIntToHandle(info[0]);

  r_cb = Napi::Persistent(info[1].As<Napi::Function>());
  const auto ret = mylib_set_callback(handle, onEvent, nullptr);
  return env.Null();
}

This works (the JS callback is run), but the problem is that the callback is global. If I have ctx2 and call mylib_set_event_callback again, it will overwrite the callback from ctx1.

How can I convert this so that callbacks for ctx1 and ctx2 will both be called?

Alex Shaw
  • 163
  • 1
  • 7

1 Answers1

0

Your callback is global because it is in a global variable.

You should place the Napi::FunctionReference in the mylib_t structure.

If you cannot modify that structure, I see that you can pass a a context pointer in userdata - you are passing nullptr. Create dynamically a Napi::FunctionReference with new and pass this pointer so that you can have it in onEvent.

You should also properly the reference when the structure is destroyed or the function is replaced.

mmomtchev
  • 2,497
  • 1
  • 8
  • 23