Skip to content

Commit a7a5352

Browse files
committed
test: v4,v5+ tests pass
1 parent 3b24e74 commit a7a5352

File tree

7 files changed

+165
-79
lines changed

7 files changed

+165
-79
lines changed

napi-inl.h

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,27 @@ static inline CallJsWrapper(napi_env env, napi_value jsCallback, void * /*contex
214214
}
215215
}
216216

217-
template <typename CallbackType>
218-
typename ThreadSafeFunctionEx<>::DefaultFunctionType
219-
DefaultCallbackWrapper(
220-
napi_env env, CallbackType cb) {
221-
return ThreadSafeFunctionEx<>::DefaultFunctionFactory(env);
217+
#if NAPI_VERSION > 4
218+
219+
template <typename CallbackType, typename TSFN>
220+
napi_value DefaultCallbackWrapper(napi_env /*env*/, std::nullptr_t /*cb*/) {
221+
return nullptr;
222+
}
223+
224+
template <typename CallbackType, typename TSFN>
225+
napi_value DefaultCallbackWrapper(napi_env /*env*/, Napi::Function cb) {
226+
return cb;
222227
}
223228

229+
#else
230+
template <typename CallbackType, typename TSFN>
231+
napi_value DefaultCallbackWrapper(napi_env env, Napi::Function cb) {
232+
if (cb.IsEmpty()) {
233+
return TSFN::EmptyFunctionFactory(env);
234+
}
235+
return cb;
236+
}
237+
#endif
224238
#endif
225239

226240
template <typename Getter, typename Setter>
@@ -4524,8 +4538,9 @@ ThreadSafeFunctionEx<ContextType, DataType, CallJs>::New(
45244538
FinalizerDataType>(
45254539
{data, finalizeCallback});
45264540
napi_status status = napi_create_threadsafe_function(
4527-
env, details::DefaultCallbackWrapper<CallbackType>(env, callback), resource, String::From(env, resourceName), maxQueueSize,
4528-
initialThreadCount, finalizeData,
4541+
env, details::DefaultCallbackWrapper<CallbackType, ThreadSafeFunctionEx<ContextType, DataType, CallJs>>(env, callback), resource,
4542+
String::From(env, resourceName), maxQueueSize, initialThreadCount,
4543+
finalizeData,
45294544
details::ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>::
45304545
FinalizeFinalizeWrapperWithDataAndContext,
45314546
context, CallJsInternal, &tsfn._tsfn);
@@ -4634,19 +4649,48 @@ void ThreadSafeFunctionEx<ContextType, DataType, CallJs>::CallJsInternal(
46344649
env, jsCallback, context, data);
46354650
}
46364651

4652+
#if NAPI_VERSION == 4
46374653
// static
46384654
template <typename ContextType, typename DataType,
46394655
void (*CallJs)(Napi::Env, Napi::Function, ContextType *, DataType *)>
4640-
typename ThreadSafeFunctionEx<ContextType, DataType, CallJs>::DefaultFunctionType
4641-
ThreadSafeFunctionEx<ContextType, DataType, CallJs>::DefaultFunctionFactory(
4656+
Napi::Function
4657+
ThreadSafeFunctionEx<ContextType, DataType, CallJs>::EmptyFunctionFactory(
46424658
Napi::Env env) {
4643-
#if NAPI_VERSION > 4
4644-
return nullptr;
4659+
return Napi::Function::New(env, [](const CallbackInfo &cb) {});
4660+
}
4661+
4662+
// static
4663+
template <typename ContextType, typename DataType,
4664+
void (*CallJs)(Napi::Env, Napi::Function, ContextType *, DataType *)>
4665+
Napi::Function
4666+
ThreadSafeFunctionEx<ContextType, DataType, CallJs>::FunctionOrEmpty(
4667+
Napi::Env env, Napi::Function &callback) {
4668+
if (callback.IsEmpty()) {
4669+
return EmptyFunctionFactory(env);
4670+
}
4671+
return callback;
4672+
}
4673+
46454674
#else
4646-
return Function::New(env, [](const CallbackInfo &cb) {});
4647-
#endif
4675+
// static
4676+
template <typename ContextType, typename DataType,
4677+
void (*CallJs)(Napi::Env, Napi::Function, ContextType *, DataType *)>
4678+
std::nullptr_t
4679+
ThreadSafeFunctionEx<ContextType, DataType, CallJs>::EmptyFunctionFactory(
4680+
Napi::Env /*env*/) {
4681+
return nullptr;
46484682
}
46494683

4684+
// static
4685+
template <typename ContextType, typename DataType,
4686+
void (*CallJs)(Napi::Env, Napi::Function, ContextType *, DataType *)>
4687+
Napi::Function
4688+
ThreadSafeFunctionEx<ContextType, DataType, CallJs>::FunctionOrEmpty(
4689+
Napi::Env /*env*/, Napi::Function &callback) {
4690+
return callback;
4691+
}
4692+
4693+
#endif
46504694

46514695
////////////////////////////////////////////////////////////////////////////////
46524696
// ThreadSafeFunction class

napi.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,17 +2051,20 @@ namespace Napi {
20512051
class ThreadSafeFunctionEx {
20522052

20532053
public:
2054-
#if NAPI_VERSION > 4
2055-
using DefaultFunctionType = std::nullptr_t;
2056-
#else
2057-
using DefaultFunctionType = const Napi::Function;
2058-
#endif
2054+
20592055
// This API may only be called from the main thread.
20602056
// Helper function that returns nullptr if running N-API 5+, otherwise a
20612057
// non-empty, no-op Function. This provides the ability to specify at
20622058
// compile-time a callback parameter to `New` that safely does no action
20632059
// when targeting _any_ N-API version.
2064-
static DefaultFunctionType DefaultFunctionFactory(Napi::Env env);
2060+
#if NAPI_VERSION > 4
2061+
static std::nullptr_t EmptyFunctionFactory(Napi::Env env);
2062+
#else
2063+
static Napi::Function EmptyFunctionFactory(Napi::Env env);
2064+
#endif
2065+
static Napi::Function FunctionOrEmpty(Napi::Env env, Napi::Function& callback);
2066+
2067+
20652068

20662069

20672070
#if NAPI_VERSION > 4

test/threadsafe_function_ex/test/basic.cc

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class TSFNWrap : public base {
5454
}
5555

5656
static std::array<ClassPropertyDescriptor<TSFNWrap>, 2> InstanceMethods() {
57-
return {InstanceMethod("release", &TSFNWrap::Release),
58-
InstanceMethod("call", &TSFNWrap::Call)};
57+
return {{InstanceMethod("release", &TSFNWrap::Release),
58+
InstanceMethod("call", &TSFNWrap::Call)}};
5959
}
6060

6161
Napi::Value Call(const CallbackInfo &info) {
@@ -105,23 +105,23 @@ class TSFNWrap : public base {
105105

106106
ContextType *context = new ContextType(Persistent(info[0]));
107107

108-
_tsfn = TSFN::New(
109-
env, // napi_env env,
110-
TSFN::DefaultFunctionFactory(env), // const Function& callback,
111-
Value(), // const Object& resource,
112-
"Test", // ResourceString resourceName,
113-
0, // size_t maxQueueSize,
114-
1, // size_t initialThreadCount,
115-
context, // ContextType* context,
116-
base::Finalizer, // Finalizer finalizer
117-
&_deferred // FinalizerDataType* data
118-
);
108+
_tsfn =
109+
TSFN::New(env, // napi_env env,
110+
TSFN::EmptyFunctionFactory(env), // const Function& callback,
111+
Value(), // const Object& resource,
112+
"Test", // ResourceString resourceName,
113+
0, // size_t maxQueueSize,
114+
1, // size_t initialThreadCount,
115+
context, // ContextType* context,
116+
base::Finalizer, // Finalizer finalizer
117+
&_deferred // FinalizerDataType* data
118+
);
119119
}
120120

121121
static std::array<ClassPropertyDescriptor<TSFNWrap>, 3> InstanceMethods() {
122-
return {InstanceMethod("call", &TSFNWrap::Call),
123-
InstanceMethod("getContext", &TSFNWrap::GetContext),
124-
InstanceMethod("release", &TSFNWrap::Release)};
122+
return {{InstanceMethod("call", &TSFNWrap::Call),
123+
InstanceMethod("getContext", &TSFNWrap::GetContext),
124+
InstanceMethod("release", &TSFNWrap::Release)}};
125125
}
126126

127127
Napi::Value Call(const CallbackInfo &info) {
@@ -187,8 +187,8 @@ class TSFNWrap : public base {
187187
}
188188

189189
static std::array<ClassPropertyDescriptor<TSFNWrap>, 2> InstanceMethods() {
190-
return {InstanceMethod("release", &TSFNWrap::Release),
191-
InstanceMethod("call", &TSFNWrap::Call)};
190+
return {{InstanceMethod("release", &TSFNWrap::Release),
191+
InstanceMethod("call", &TSFNWrap::Call)}};
192192
}
193193

194194
Napi::Value Call(const CallbackInfo &info) {
@@ -212,14 +212,17 @@ struct DataType {
212212
// CallJs callback function provided to `napi_create_threadsafe_function`. It is
213213
// _NOT_ used by `Napi::ThreadSafeFunctionEx<>`, which is why these arguments
214214
// are napi_*.
215-
static void CallJs(napi_env env, napi_value jsCallback, void * /*context*/,
215+
static void CallJs(napi_env env, napi_value /*jsCallback*/, void * /*context*/,
216216
void *data) {
217217
DataType *casted = static_cast<DataType *>(data);
218218
if (env != nullptr) {
219219
if (data != nullptr) {
220220
napi_value undefined;
221221
napi_status status = napi_get_undefined(env, &undefined);
222-
NAPI_THROW_IF_FAILED(env, status);
222+
if (status != napi_ok) {
223+
NAPI_THROW_VOID(
224+
Error::New(env, "Could not get undefined from environment"));
225+
}
223226
if (casted->reject) {
224227
casted->deferred.Reject(undefined);
225228
} else {
@@ -257,14 +260,14 @@ class TSFNWrap : public base {
257260
napi_threadsafe_function napi_tsfn;
258261

259262
// A threadsafe function on N-API 4 still requires a callback function, so
260-
// this uses the `DefaultFunctionFactory` helper method to return a no-op
263+
// this uses the `EmptyFunctionFactory` helper method to return a no-op
261264
// Function.
262265
auto status = napi_create_threadsafe_function(
263-
info.Env(), TSFN::DefaultFunctionFactory(env), nullptr,
266+
info.Env(), TSFN::EmptyFunctionFactory(env), nullptr,
264267
String::From(info.Env(), "Test"), 0, 1, nullptr, nullptr, nullptr,
265268
CallJs, &napi_tsfn);
266269
if (status != napi_ok) {
267-
NAPI_THROW_IF_FAILED(env, status);
270+
NAPI_THROW_VOID(Error::New(env, "Could not create TSFN."));
268271
}
269272
_tsfn = TSFN(napi_tsfn);
270273
#else
@@ -276,15 +279,16 @@ class TSFNWrap : public base {
276279
info.Env(), nullptr, nullptr, String::From(info.Env(), "Test"), 0, 1,
277280
nullptr, Finalizer, this, CallJs, &napi_tsfn);
278281
if (status != napi_ok) {
279-
NAPI_THROW_IF_FAILED(env, status);
282+
NAPI_THROW_VOID(
283+
Error::New(env, "Could not get undefined from environment"));
280284
}
281285
_tsfn = TSFN(napi_tsfn);
282286
#endif
283287
}
284288

285289
static std::array<ClassPropertyDescriptor<TSFNWrap>, 2> InstanceMethods() {
286-
return {InstanceMethod("release", &TSFNWrap::Release),
287-
InstanceMethod("call", &TSFNWrap::Call)};
290+
return {{InstanceMethod("release", &TSFNWrap::Release),
291+
InstanceMethod("call", &TSFNWrap::Call)}};
288292
}
289293

290294
Napi::Value Call(const CallbackInfo &info) {
@@ -333,7 +337,7 @@ class TSFNWrap : public base {
333337
// A threadsafe function on N-API 4 still requires a callback function.
334338
_tsfn = TSFN::New(
335339
env, // napi_env env,
336-
TSFN::DefaultFunctionFactory(
340+
TSFN::EmptyFunctionFactory(
337341
env), // N-API 5+: nullptr; else: const Function& callback,
338342
"Test", // ResourceString resourceName,
339343
1, // size_t maxQueueSize,
@@ -349,8 +353,8 @@ class TSFNWrap : public base {
349353
}
350354

351355
static std::array<ClassPropertyDescriptor<TSFNWrap>, 2> InstanceMethods() {
352-
return {InstanceMethod("release", &TSFNWrap::Release),
353-
InstanceMethod("call", &TSFNWrap::Call)};
356+
return {{InstanceMethod("release", &TSFNWrap::Release),
357+
InstanceMethod("call", &TSFNWrap::Call)}};
354358
}
355359

356360
// Since this test spec has no CALLBACK, CONTEXT, or FINALIZER. We have no way

test/threadsafe_function_ex/test/example.cc

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,40 @@ class TSFNWrap;
4343
// Context of the TSFN.
4444
using Context = TSFNWrap;
4545

46+
// Data passed (as pointer) to [Non]BlockingCall
4647
struct Data {
47-
// Data passed (as pointer) to [Non]BlockingCall
4848
std::promise<int> promise;
49+
uint32_t threadId;
50+
bool logCall;
4951
uint32_t base;
5052
};
5153
using DataType = std::unique_ptr<Data>;
5254

5355
// CallJs callback function
54-
static void CallJs(Napi::Env env, Napi::Function /*jsCallback*/,
55-
Context *context, DataType *dataPtr) {
56+
static void CallJs(Napi::Env env, Napi::Function jsCallback,
57+
Context * /*context*/, DataType *dataPtr) {
5658
if (dataPtr != nullptr) {
5759
auto &data = *dataPtr;
5860
if (env != nullptr) {
59-
data->promise.set_value(data->base * data->base);
61+
auto calculated = data->base * data->base;
62+
if (!jsCallback.IsEmpty()) {
63+
auto value = jsCallback.Call({Number::New(env, data->threadId), Number::New(env, calculated)});
64+
if (env.IsExceptionPending()) {
65+
const auto &error = env.GetAndClearPendingException();
66+
data->promise.set_exception(
67+
std::make_exception_ptr(std::runtime_error(error.Message())));
68+
} else if (value.IsNumber()) {
69+
calculated = value.ToNumber();
70+
}
71+
}
72+
if (data->logCall) {
73+
std::string message("Thread " + std::to_string(data->threadId) +
74+
" CallJs resolving std::promise");
75+
auto console = env.Global().Get("console").As<Object>();
76+
console.Get("log").As<Function>().Call(console,
77+
{String::New(env, message)});
78+
}
79+
data->promise.set_value(calculated);
6080
} else {
6181
data->promise.set_exception(std::make_exception_ptr(
6282
std::runtime_error("TSFN has been finalized.")));
@@ -106,9 +126,10 @@ class TSFNWrap : public base {
106126
// TSFNWrap object gets garbage-collected and there are still active threads.
107127
using FinalizerDataType = std::shared_ptr<FinalizerData>;
108128

109-
#define THREADLOG(X) if (context->logThread) {\
110-
std::cout << X;\
111-
}
129+
#define THREADLOG(X) \
130+
if (context->logThread) { \
131+
std::cout << X; \
132+
}
112133
static void threadEntry(size_t threadId, TSFN tsfn, uint32_t callCount,
113134
Context *context) {
114135
using namespace std::chrono_literals;
@@ -118,7 +139,10 @@ std::cout << X;\
118139
for (auto i = 0U; i < callCount; ++i) {
119140
auto data = std::make_unique<Data>();
120141
data->base = threadId + 1;
121-
THREADLOG("Thread " << threadId << " making call, base = " << data->base << "\n")
142+
data->threadId = threadId;
143+
data->logCall = context->logCall;
144+
THREADLOG("Thread " << threadId << " making call, base = " << data->base
145+
<< "\n")
122146

123147
tsfn.NonBlockingCall(&data);
124148
auto future = data->promise.get_future();
@@ -133,14 +157,15 @@ std::cout << X;\
133157
#undef THREADLOG
134158

135159
static std::array<ClassPropertyDescriptor<TSFNWrap>, 4> InstanceMethods() {
136-
return {InstanceMethod("getContext", &TSFNWrap::GetContext),
137-
InstanceMethod("start", &TSFNWrap::Start),
138-
InstanceMethod("callCount", &TSFNWrap::CallCount),
139-
InstanceMethod("release", &TSFNWrap::Release)};
160+
return {{InstanceMethod("getContext", &TSFNWrap::GetContext),
161+
InstanceMethod("start", &TSFNWrap::Start),
162+
InstanceMethod("callCount", &TSFNWrap::CallCount),
163+
InstanceMethod("release", &TSFNWrap::Release)}};
140164
}
141165

142166
bool cppExceptions = false;
143167
bool logThread;
168+
bool logCall;
144169
std::atomic_uint succeededCalls;
145170
std::atomic_int aggregate;
146171

@@ -163,7 +188,6 @@ std::cout << X;\
163188
finalizerData = std::make_shared<FinalizerData>();
164189

165190
logThread = DefaultOptions.logThread;
166-
bool logCall = DefaultOptions.logCall;
167191

168192
if (info.Length() > 0 && info[0].IsObject()) {
169193
auto arg0 = info[0].ToObject();
@@ -239,11 +263,11 @@ std::cout << X;\
239263
succeededCalls = 0;
240264
aggregate = 0;
241265
_tsfn = TSFN::New(
242-
env, // napi_env env,
243-
TSFN::DefaultFunctionFactory(env), // const Function& callback,
244-
Value(), // const Object& resource,
245-
"Test", // ResourceString resourceName,
246-
0, // size_t maxQueueSize,
266+
env, // napi_env env,
267+
TSFN::FunctionOrEmpty(env, callback), // const Function& callback,
268+
Value(), // const Object& resource,
269+
"Test", // ResourceString resourceName,
270+
0, // size_t maxQueueSize,
247271
threadCount + 1, // size_t initialThreadCount, +1 for Node thread
248272
this, // Context* context,
249273
Finalizer, // Finalizer finalizer
@@ -255,7 +279,7 @@ std::cout << X;\
255279
callCounts[threadId], this));
256280
}
257281

258-
return String::New(env, "started");
282+
return Number::New(env, threadCount);
259283
};
260284

261285
// TSFN finalizer. Joins the threads and resolves the Promise returned by

0 commit comments

Comments
 (0)