I've found that using ThreadSafeFunction in an ObjectWrap class blocks the event loop from exiting even after the program has finished. As soon as I remove the functions which use ThreadSafeFunction (onScanStart and onScanStop), it exits correctly. But when ThreadSafeFunction is used, the program continues running until I hit CTRL+C.
Here's part of the code I'm using. I'd appreciate any help understanding what's wrong:
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);
}