diff --git a/src/native/CallbackWrapper.h b/src/native/CallbackWrapper.h index 235e01e..36656ac 100644 --- a/src/native/CallbackWrapper.h +++ b/src/native/CallbackWrapper.h @@ -1,3 +1,6 @@ +#ifndef CALLBACK_WRAPPER_H +#define CALLBACK_WRAPPER_H + #ifndef SIZE_MAX #define SIZE_MAX ((size_t)(-1)) #endif @@ -10,51 +13,99 @@ typedef std::wstring STRING; typedef std::string STRING; #endif -class CallbackWrapper : public Napi::AsyncWorker +struct CallbackData { -public: - CallbackWrapper(Napi::Function &callback) : Napi::AsyncWorker(callback) - { - } uint32_t status; + virtual ~CallbackData() = default; +}; -private: - void Execute() +struct StatusCallbackData : public CallbackData +{ + StatusCallbackData(uint32_t s) { + status = s; } +}; + +struct ReleaseUpdateCallbackData : public CallbackData +{ + STRING releaseJson; + void *userData; - void OnOK() + ReleaseUpdateCallbackData(uint32_t s, const STRING &json, void *ud) + : releaseJson(json), userData(ud) { - Napi::HandleScope scope(Env()); - Callback().Call({Napi::Number::New(Env(), status)}); + status = s; } }; -class ReleaseUpdateCallbackWrapper : public Napi::AsyncWorker +class CallbackWrapper { public: - ReleaseUpdateCallbackWrapper(Napi::Function &callback) : Napi::AsyncWorker(callback) + CallbackWrapper(Napi::Env env, Napi::Function callback) { + tsfn = Napi::ThreadSafeFunction::New( + env, + callback, + "LexActivatorCallback", + 0, // Unlimited queue + 1 // Initial thread count + ); + } + + ~CallbackWrapper() + { + tsfn.Abort(); + tsfn.Release(); + } + + void Call(uint32_t status) + { + auto *data = new StatusCallbackData(status); + napi_status napiStatus = tsfn.NonBlockingCall(data, InvokeStatusCallback); + if (napiStatus != napi_ok) + { + delete data; + } + } + + void Call(uint32_t status, const STRING &releaseJson, void *userData) + { + auto *data = new ReleaseUpdateCallbackData(status, releaseJson, userData); + napi_status napiStatus = tsfn.NonBlockingCall(data, InvokeReleaseUpdateCallback); + if (napiStatus != napi_ok) + { + delete data; + } } - uint32_t status; - STRING releaseJson; - void* userData; private: - void Execute() + Napi::ThreadSafeFunction tsfn; + + static void InvokeStatusCallback(Napi::Env env, Napi::Function jsCallback, StatusCallbackData *data) { + if (env != nullptr && jsCallback != nullptr && data != nullptr) + { + Napi::HandleScope scope(env); + jsCallback.Call({Napi::Number::New(env, data->status)}); + } + delete data; } - void OnOK() + static void InvokeReleaseUpdateCallback(Napi::Env env, Napi::Function jsCallback, ReleaseUpdateCallbackData *data) { - Napi::HandleScope scope(Env()); + if (env != nullptr && jsCallback != nullptr && data != nullptr) + { + Napi::HandleScope scope(env); #ifdef _WIN32 - // convert STRING to utf16 encoded string - const wchar_t *releaseJsonPtr = releaseJson.c_str(); - const char16_t *utf16StringPtr = reinterpret_cast(releaseJsonPtr); - Callback().Call({Napi::Number::New(Env(), status), Napi::String::New(Env(), utf16StringPtr), Napi::External::New(Env(), userData)}); + const wchar_t *releaseJsonPtr = data->releaseJson.c_str(); + const char16_t *utf16StringPtr = reinterpret_cast(releaseJsonPtr); + jsCallback.Call({Napi::Number::New(env, data->status), Napi::String::New(env, utf16StringPtr),Napi::External::New(env, data->userData)}); #else - Callback().Call({Napi::Number::New(Env(), status), Napi::String::New(Env(), releaseJson), Napi::External::New(Env(), userData)}); + jsCallback.Call({Napi::Number::New(env, data->status), Napi::String::New(env, data->releaseJson), Napi::External::New(env, data->userData)}); #endif + } + delete data; } -}; \ No newline at end of file +}; +#endif // CALLBACK_WRAPPER_H diff --git a/src/native/main.cpp b/src/native/main.cpp index 2620cb6..a2d1693 100644 --- a/src/native/main.cpp +++ b/src/native/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using namespace ::std; @@ -23,8 +24,9 @@ const char *MISSING_LICENSE_KEY = "License key not set"; map LicenseCallbacks; map ReleaseCallbacks; -map ReleaseUpdateCallbacks; +map ReleaseUpdateCallbacks; +std::mutex callbackMutex; STRING toEncodedString(Napi::String input) { @@ -44,8 +46,14 @@ void licenseCallback(uint32_t status) { return; } - LicenseCallbacks[STRING(licenseKey)]->status = status; - LicenseCallbacks[STRING(licenseKey)]->Queue(); + { + std::lock_guard lock(callbackMutex); + auto it = LicenseCallbacks.find(STRING(licenseKey)); + if (it != LicenseCallbacks.end() && it->second != nullptr) + { + it->second->Call(status); + } + } } void softwareReleaseUpdateCallback(uint32_t status) @@ -55,21 +63,31 @@ void softwareReleaseUpdateCallback(uint32_t status) { return; } - ReleaseCallbacks[STRING(licenseKey)]->status = status; - ReleaseCallbacks[STRING(licenseKey)]->Queue(); + { + std::lock_guard lock(callbackMutex); + auto it = ReleaseCallbacks.find(STRING(licenseKey)); + if (it != ReleaseCallbacks.end() && it->second != nullptr) + { + it->second->Call(status); + } + } } void releaseUpdateCallback(uint32_t status, CONST_CHARTYPE releaseJson, void* userData) -{ +{ CHARTYPE licenseKey[256]; if (GetLicenseKey(licenseKey, 256) != LA_OK) { return; } - ReleaseUpdateCallbacks[STRING(licenseKey)]->status = status; - ReleaseUpdateCallbacks[STRING(licenseKey)]->releaseJson.assign(releaseJson); - ReleaseUpdateCallbacks[STRING(licenseKey)]->userData = NULL; - ReleaseUpdateCallbacks[STRING(licenseKey)]->Queue(); + { + std::lock_guard lock(callbackMutex); + auto it = ReleaseUpdateCallbacks.find(STRING(licenseKey)); + if (it != ReleaseUpdateCallbacks.end() && it->second != nullptr) + { + it->second->Call(status, STRING(releaseJson), userData); + } + } } Napi::Value setProductFile(const Napi::CallbackInfo &info) @@ -257,8 +275,17 @@ Napi::Value setLicenseCallback(const Napi::CallbackInfo &info) Napi::Error::New(env, MISSING_LICENSE_KEY).ThrowAsJavaScriptException(); return env.Null(); } - LicenseCallbacks[STRING(licenseKey)] = new CallbackWrapper(callback); - LicenseCallbacks[STRING(licenseKey)]->SuppressDestruct(); + STRING key(licenseKey); + // Clean up existing callback if present + { + std::lock_guard lock(callbackMutex); + auto it = LicenseCallbacks.find(key); + if (it != LicenseCallbacks.end() && it->second != nullptr) + { + delete it->second; + } + LicenseCallbacks[key] = new CallbackWrapper(env, callback); + } return Napi::Number::New(env, SetLicenseCallback(licenseCallback)); } @@ -1355,8 +1382,17 @@ Napi::Value checkReleaseUpdate(const Napi::CallbackInfo &info) Napi::Error::New(env, MISSING_LICENSE_KEY).ThrowAsJavaScriptException(); return env.Null(); } - ReleaseUpdateCallbacks[STRING(licenseKey)] = new ReleaseUpdateCallbackWrapper(callback); - ReleaseUpdateCallbacks[STRING(licenseKey)]->SuppressDestruct(); + STRING key(licenseKey); + // Clean up existing callback if present + { + std::lock_guard lock(callbackMutex); + auto it = ReleaseUpdateCallbacks.find(key); + if (it != ReleaseUpdateCallbacks.end() && it->second != nullptr) + { + delete it->second; + } + ReleaseUpdateCallbacks[key] = new CallbackWrapper(env, callback); + } return Napi::Number::New(env, CheckReleaseUpdateInternal(releaseUpdateCallback, arg1, NULL)); } @@ -1399,8 +1435,17 @@ Napi::Value checkForReleaseUpdate(const Napi::CallbackInfo &info) Napi::Error::New(env, MISSING_LICENSE_KEY).ThrowAsJavaScriptException(); return env.Null(); } - ReleaseCallbacks[STRING(licenseKey)] = new CallbackWrapper(callback); - ReleaseCallbacks[STRING(licenseKey)]->SuppressDestruct(); + STRING key(licenseKey); + // Clean up existing callback if present + { + std::lock_guard lock(callbackMutex); + auto it = ReleaseCallbacks.find(key); + if (it != ReleaseCallbacks.end() && it->second != nullptr) + { + delete it->second; + } + ReleaseCallbacks[key] = new CallbackWrapper(env, callback); + } return Napi::Number::New(env, CheckForReleaseUpdate(arg0.c_str(), arg1.c_str(), arg2.c_str(), softwareReleaseUpdateCallback)); }