NodeJS 为什么带有ObjectWrap的Napi::ThreadSafeFunction阻止事件循环退出?

hk8txs48  于 2023-06-05  发布在  Node.js
关注(0)|答案(1)|浏览(208)

我发现在ObjectWrap类中使用ThreadSafeFunction会阻止事件循环退出,即使程序已经完成。只要我删除使用ThreadSafeFunction的函数(onScanStart和onScanStop),它就可以正确退出。但是当使用ThreadSafeFunction时,程序会继续运行,直到我按下CTRL+C。
下面是我使用的部分代码。如果你能帮我找出问题所在我会很感激

class AdapterWrapper : public Napi::ObjectWrap<Adapter> {
public:
  static Napi::Object Init(Napi::Env env, Napi::Object exports);
  AdapterWrapper(const Napi::CallbackInfo &info);
  ~AdapterWrapper();

  static Napi::FunctionReference constructor;

private:
  adapter_t handle;
  Napi::ThreadSafeFunction onScanStartFn;
  Napi::ThreadSafeFunction onScanStopFn;

  static void onScanStart(adapter_t handle, void *userdata);
  static void onScanStop(adapter_t handle, void *userdata);

  Napi::Value Scan(const Napi::CallbackInfo &info);
  void SetOnScanStart(const Napi::CallbackInfo &info);
  void SetOnScanStop(const Napi::CallbackInfo &info);
};

Napi::FunctionReference AdapterWrapper::constructor;

Napi::Object AdapterWrapper::Init(Napi::Env env, Napi::Object exports) {
  Napi::Function func = DefineClass(env, "Adapter", {
    InstanceMethod("scan", &AdapterWrapper::ScanStart),
    InstanceMethod("setOnScanStart", &AdapterWrapper::SetOnScanStart),
    InstanceMethod("setOnScanStop", &AdapterWrapper::SetOnScanStop)
  });

  constructor = Napi::Persistent(func);
  constructor.SuppressDestruct();

  exports.Set("Adapter", func);
  return exports;
}

AdapterWrapper::AdapterWrapper(const Napi::CallbackInfo &info)
    : Napi::ObjectWrap<AdapterWrapper>(info) {
  Napi::Env env = info.Env();

  this->handle = adapter_get_handle();
}

AdapterWrapper::~AdapterWrapper() {
  adapter_release_handle(this->handle);
  if (this->onScanStartFn) {
    this->onScanStartFn.Release();
  }
  if (this->onScanStopFn) {
    this->onScanStopFn.Release();
  }
}

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

  this->onScanStartFn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "onScanStartFn", 0, 1);

  adapter_set_on_scan_start(this->handle, onScanStart, this);
}

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

  this->onScanStopFn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "onScanStopFn", 0, 1);

  adapter_set_on_scan_stop(this->handle, onScanStop, this);
}

void AdapterWrapper::onScanStart(adapter_t handle, void *userdata) {
  auto adapter = reinterpret_cast<AdapterWrapper *>(userdata);
  auto callback = [](Napi::Env env, Napi::Function jsCallback) {
    jsCallback.Call({});
  };
  onScanStartFn.BlockingCall(callback);
}

void AdapterWrapper::onScanStop(adapter_t handle, void *userdata) {
  auto adapter = reinterpret_cast<AdapterWrapper *>(userdata);
  auto callback = [](Napi::Env env, Napi::Function jsCallback) {
    jsCallback.Call({});
  };
  adapter->onScanStopFn.BlockingCall(callback);
}
xmq68pz9

xmq68pz91#

答案是在创建每个ThreadSafeFunction之后调用Unref。这告诉事件循环,一旦程序完成,它就可以被垃圾回收,而不会改变行为。HandleScope也是为了额外的安全性而添加的。
修改后的源代码:

class AdapterWrapper : public Napi::ObjectWrap<Adapter> {
public:
  static Napi::Object Init(Napi::Env env, Napi::Object exports);
  AdapterWrapper(const Napi::CallbackInfo &info);
  ~AdapterWrapper();

  static Napi::FunctionReference constructor;

private:
  adapter_t handle;
  Napi::ThreadSafeFunction onScanStartFn;
  Napi::ThreadSafeFunction onScanStopFn;

  static void onScanStart(adapter_t handle, void *userdata);
  static void onScanStop(adapter_t handle, void *userdata);

  Napi::Value Scan(const Napi::CallbackInfo &info);
  void SetOnScanStart(const Napi::CallbackInfo &info);
  void SetOnScanStop(const Napi::CallbackInfo &info);
};

Napi::FunctionReference AdapterWrapper::constructor;

Napi::Object AdapterWrapper::Init(Napi::Env env, Napi::Object exports) {
  Napi::Function func = DefineClass(env, "Adapter", {
    InstanceMethod("scan", &AdapterWrapper::ScanStart),
    InstanceMethod("setOnScanStart", &AdapterWrapper::SetOnScanStart),
    InstanceMethod("setOnScanStop", &AdapterWrapper::SetOnScanStop)
  });

  constructor = Napi::Persistent(func);
  constructor.SuppressDestruct();

  exports.Set("Adapter", func);
  return exports;
}

AdapterWrapper::AdapterWrapper(const Napi::CallbackInfo &info)
    : Napi::ObjectWrap<AdapterWrapper>(info) {
  Napi::Env env = info.Env();

  this->handle = adapter_get_handle();
}

AdapterWrapper::~AdapterWrapper() {
  adapter_release_handle(this->handle);
  if (this->onScanStartFn) {
    this->onScanStartFn.Release();
  }
  if (this->onScanStopFn) {
    this->onScanStopFn.Release();
  }
}

Napi::Value AdapterWrapper::SetOnScanStart(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  Napi::HandleScope scope(env);

  this->onScanStartFn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "onScanStartFn", 0, 1);
  this->onScanStartFn.Unref(env);

  adapter_set_on_scan_start(this->handle, onScanStart, this);
}

Napi::Value AdapterWrapper::SetOnScanStop(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();
  Napi::HandleScope scope(env);

  this->onScanStopFn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "onScanStopFn", 0, 1);
  this->onScanStopFn.Unref(env);

  adapter_set_on_scan_stop(this->handle, onScanStop, this);
}

void AdapterWrapper::onScanStart(adapter_t handle, void *userdata) {
  auto adapter = reinterpret_cast<AdapterWrapper *>(userdata);
  auto callback = [](Napi::Env env, Napi::Function jsCallback) {
    jsCallback.Call({});
  };
  onScanStartFn.BlockingCall(callback);
}

void AdapterWrapper::onScanStop(adapter_t handle, void *userdata) {
  auto adapter = reinterpret_cast<AdapterWrapper *>(userdata);
  auto callback = [](Napi::Env env, Napi::Function jsCallback) {
    jsCallback.Call({});
  };
  adapter->onScanStopFn.BlockingCall(callback);
}

相关问题