How to properly handle Object/Buffer/Uint8Array in AsyncWorker?

75 Views Asked by At

I couldn't find in the documentation and examples how to properly handle Object/Buffer/Uint8Array parameters in AsyncWorker?

The task: I'd like to receive data as Uint8Array and use it in AsyncWorker.

If I understand correctly, the data can be garbage collected.

Should I:

  • Wrap object in ObjectReference and pass it to AsyncWorker constructor?
  • Wrap Uint8Array in ObjectReference`?
  • Wrap uint8_t* data as something?

Naive code:

#include "napi.h"

class DoHeavyMathWorker : public Napi::AsyncWorker {
  public:
    DoHeavyMathWorker(const Napi::Env& env, uint8_t* data)
      : Napi::AsyncWorker{env, "DoHeavyMathWorker"},
      m_deferred{env},
      m_data{data}
      {}

    Napi::Promise GetPromise() { return m_deferred.Promise(); }

  protected:
    void Execute() {
      // heavy math with data
      if (m_data[0] == 0) {
        m_result = 1;
      } else {
        m_result = 2;
      }
    }

    void OnOK() { m_deferred.Resolve(Napi::Number::New(Env(), m_result)); }
    void OnError(const Napi::Error& err) { m_deferred.Reject(err.Value()); }

  private:
    Napi::Promise::Deferred m_deferred;
    uint8_t* m_data;
    uint32_t m_result;
};

Napi::Value DoHeavyMath(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (!info[0].IsObject()) {
    Napi::TypeError::New(env, "arg must be an object")
        .ThrowAsJavaScriptException();
    return env.Undefined();
  }
  Napi::Object obj = info[0].As<Napi::Object>();
  uint8_t* data = obj.Get("data").As<Napi::Uint8Array>().Data();

  DoHeavyMathWorker* worker = new DoHeavyMathWorker(env, data);
  worker->Queue();
  return worker->GetPromise();
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set("doHeavyMath", Napi::Function::New(env, DoHeavyMath));
  return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

JS part:

const result = await addon.doHeavyMath({
  data: new Uint8Array([42]),
});
console.log('result:', result);

1

There are 1 best solutions below

2
Criminal_Affair_At_SO On

You have to create a persistent reference and keep it around for as long as you need the data. Note, that persistent references can only be created and destroyed on the main thread.

You should create it on the heap in the function that creates the AsyncWorker:

auto *persistent = new Napi::ObjectReference(
  Napi::Persistent(array_buffer.As<Napi::Object>()));

Pass this reference to the AsyncWorker and delete it in the AsyncWorker callbacks - OnOK and OnError:

// This is everything needed to free the underlying reference
delete persistent;