Skip to content
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

emit event as thread safe callback #9

Open
corymickelson opened this issue Dec 23, 2018 · 5 comments
Open

emit event as thread safe callback #9

corymickelson opened this issue Dec 23, 2018 · 5 comments

Comments

@corymickelson
Copy link

corymickelson commented Dec 23, 2018

Hello, first thank you for publishing this module. I am trying to call the emit function on a ObjectWrapped class that inherits from EventEmitter, in which i am passing to ThreadSafeCallback the emit function. Stepping through the code I can see the cb->call(...) is getting called but the event is never making it back to the javascript side. Below is the code in question. Any help is welcome, thank you.

Napi::Value
Signer::ReadForSignature(const CallbackInfo& info)
{
  vector<int> opts =
    AssertCallbackInfo(info, { { 0, { nullopt, option(napi_number) } } });
  if (opts[0] == 1) {
    signer->SetSignatureSize(info[0].As<Number>().Uint32Value());
  } else {
    signer->SetSignatureSize(4096);
  }

  auto emit = this->Value().Get("emit").As<Function>(); // <--  get the emit fn
  auto cb = make_shared<ThreadSafeCallback>(emit); // <-- pass emit to threadsafecallback
  field->SetSignature(*signer->GetSignatureBeacon());
  doc.WriteUpdate(signer.get(), !output.empty());
  if (!signer->HasSignaturePosition()) {
    Error::New(info.Env(),
               "Cannot find signature position in the document data")
      .ThrowAsJavaScriptException();
    return {};
  }
  signer->AdjustByteRange();
  signer->Seek(0);
  auto shareSigner = signer.get();
  thread([cb, shareSigner] {
    size_t sigBuffer = 65535 * 2, sigBufferLen;
    char* sigData;
    while (static_cast<void>(sigData = reinterpret_cast<char*>(
                               malloc(sizeof(char) * sigBuffer))),
           !sigData) {
      sigBuffer = sigBuffer / 2;
      if (!sigBuffer)
        break;
    }
    if (!sigData) {
      cb->call([](Napi::Env env, vector<napi_value>& args) {
        args = { String::New(env, "error"), String::New(env, "out of memory") };
      });
      return;
    }
    while (sigBufferLen = shareSigner->ReadForSignature(sigData, sigBuffer),
           sigBufferLen > 0) {
      cb->call([sigData, sigBuffer](Napi::Env env, vector<napi_value>& args) {
        args = { String::New(env, "data"),
                 Buffer<char>::Copy(env, sigData, sigBuffer) };
      });
    }
    free(sigData);
    cb->call([](Napi::Env env, vector<napi_value>& args) {
      args = { String::New(env, "end"), env.Undefined() };
    });
  })
    .detach();
  return this->Value();
}
....
let signer = new npdf.Signer(doc, signedPath)
signer.signatureField = field
let buf: Buffer
signer.readForSignature() // .on funcs never are invoked
signer.on('data', chunk => {
         buf = Buffer.concat(chunk)
    })
signer.on('end', () => {
        console.log(buf.length)
        return resolve()
    })
signer.on('error', e => {
        console.log(e)
        return reject(e)
    })
@mika-fischer
Copy link
Owner

Hey, sorry for the late response, was on vacation…

I think the problem might be that the callback is invoked before the listeners are attached. Could you check this? Put a printf inside the lamda before args = { …. And on the JS side, also sprinkle some console.log's. Or just call readForSignature after attaching the listeners.

Also, N-API now has native support for what this project does, so it might be wiser to use that instead:
https://nodejs.org/dist/latest-v11.x/docs/api/n-api.html#n_api_asynchronous_thread_safe_function_calls

@mika-fischer
Copy link
Owner

Additionally, I think you need to use the receiver constructor of ThreadSafeCallback and provide this->Value() as the receiver for the callback, otherwise this is undefined in the emit function...

@jonathan-roy
Copy link

Hi @mika-fischer,

I am having a similar issue, could you please elaborate on this:

you need to use the receiver constructor of ThreadSafeCallback and provide this->Value() as the receiver for the callback, otherwise this is undefined in the emit function...

Thank you!

@mika-fischer
Copy link
Owner

If in JS you would do

cb = () => val.emit()

then in C++ you need to do:

auto cb = make_shared<ThreadSafeCallback>(val, val.Get("emit").As<Function>()); // 2 arguments!

Or conversely, if you do this in C++:

auto cb = make_shared<ThreadSafeCallback>(val.Get("emit").As<Function>()); // 1 argument!

it's like doing this in JS:

cb = () => val.emit.call(undefined)

@jonathan-roy
Copy link

Ok I see! I didn't know how to set the this when calling emit, thank you very much.

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

3 participants