| #include <cstdlib> |
| #include "napi.h" |
| #include "test_helper.h" |
| |
| #if (NAPI_VERSION > 3) |
| |
| using namespace Napi; |
| |
| namespace { |
| |
| struct TestContext { |
| TestContext(Promise::Deferred&& deferred) |
| : deferred(std::move(deferred)), callData(nullptr){}; |
| |
| napi_threadsafe_function tsfn; |
| Promise::Deferred deferred; |
| double* callData; |
| |
| ~TestContext() { |
| if (callData != nullptr) delete callData; |
| }; |
| }; |
| |
| void FinalizeCB(napi_env env, void* /*finalizeData */, void* context) { |
| TestContext* testContext = static_cast<TestContext*>(context); |
| if (testContext->callData != nullptr) { |
| testContext->deferred.Resolve(Number::New(env, *testContext->callData)); |
| } else { |
| testContext->deferred.Resolve(Napi::Env(env).Undefined()); |
| } |
| delete testContext; |
| } |
| |
| void CallJSWithData(napi_env env, |
| napi_value /* callback */, |
| void* context, |
| void* data) { |
| TestContext* testContext = static_cast<TestContext*>(context); |
| testContext->callData = static_cast<double*>(data); |
| |
| napi_status status = |
| napi_release_threadsafe_function(testContext->tsfn, napi_tsfn_release); |
| |
| NAPI_THROW_IF_FAILED_VOID(env, status); |
| } |
| |
| void CallJSNoData(napi_env env, |
| napi_value /* callback */, |
| void* context, |
| void* /*data*/) { |
| TestContext* testContext = static_cast<TestContext*>(context); |
| testContext->callData = nullptr; |
| |
| napi_status status = |
| napi_release_threadsafe_function(testContext->tsfn, napi_tsfn_release); |
| |
| NAPI_THROW_IF_FAILED_VOID(env, status); |
| } |
| |
| static Value TestCall(const CallbackInfo& info) { |
| Napi::Env env = info.Env(); |
| bool isBlocking = false; |
| bool hasData = false; |
| if (info.Length() > 0) { |
| Object opts = info[0].As<Object>(); |
| bool hasProperty = MaybeUnwrap(opts.Has("blocking")); |
| if (hasProperty) { |
| isBlocking = MaybeUnwrap(MaybeUnwrap(opts.Get("blocking")).ToBoolean()); |
| } |
| hasProperty = MaybeUnwrap(opts.Has("data")); |
| if (hasProperty) { |
| hasData = MaybeUnwrap(MaybeUnwrap(opts.Get("data")).ToBoolean()); |
| } |
| } |
| |
| // Allow optional callback passed from JS. Useful for testing. |
| Function cb = Function::New(env, [](const CallbackInfo& /*info*/) {}); |
| |
| TestContext* testContext = new TestContext(Napi::Promise::Deferred(env)); |
| |
| napi_status status = |
| napi_create_threadsafe_function(env, |
| cb, |
| Object::New(env), |
| String::New(env, "Test"), |
| 0, |
| 1, |
| nullptr, /*finalize data*/ |
| FinalizeCB, |
| testContext, |
| hasData ? CallJSWithData : CallJSNoData, |
| &testContext->tsfn); |
| |
| NAPI_THROW_IF_FAILED(env, status, Value()); |
| |
| ThreadSafeFunction wrapped = ThreadSafeFunction(testContext->tsfn); |
| |
| // Test the four napi_threadsafe_function direct-accessing calls |
| if (isBlocking) { |
| if (hasData) { |
| wrapped.BlockingCall(static_cast<void*>(new double(std::rand()))); |
| } else { |
| wrapped.BlockingCall(static_cast<void*>(nullptr)); |
| } |
| } else { |
| if (hasData) { |
| wrapped.NonBlockingCall(static_cast<void*>(new double(std::rand()))); |
| } else { |
| wrapped.NonBlockingCall(static_cast<void*>(nullptr)); |
| } |
| } |
| |
| return testContext->deferred.Promise(); |
| } |
| |
| } // namespace |
| |
| Object InitThreadSafeFunctionExistingTsfn(Env env) { |
| Object exports = Object::New(env); |
| exports["testCall"] = Function::New(env, TestCall); |
| |
| return exports; |
| } |
| |
| #endif |