Skip to content

Commit 78b5a15

Browse files
authored
test: Add tests for ThreadSafeFunction's NonBlock function overloads (#1249)
* test: Add test coverage for Nonblock overloads for threadsafefunction
1 parent fdc6263 commit 78b5a15

File tree

2 files changed

+90
-20
lines changed

2 files changed

+90
-20
lines changed

test/threadsafe_function/threadsafe_function.cc

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ static std::thread threads[2];
1515
static ThreadSafeFunction s_tsfn;
1616

1717
struct ThreadSafeFunctionInfo {
18-
enum CallType { DEFAULT, BLOCKING, NON_BLOCKING } type;
18+
enum CallType {
19+
DEFAULT,
20+
BLOCKING,
21+
NON_BLOCKING,
22+
NON_BLOCKING_DEFAULT,
23+
NON_BLOCKING_SINGLE_ARG
24+
} type;
1925
bool abort;
2026
bool startSecondary;
2127
FunctionReference jsFinalizeCallback;
@@ -42,18 +48,23 @@ static void DataSourceThread() {
4248
if (s_tsfn.Acquire() != napi_ok) {
4349
Error::Fatal("DataSourceThread", "ThreadSafeFunction.Acquire() failed");
4450
}
45-
4651
threads[1] = std::thread(SecondaryThread);
4752
}
4853

4954
bool queueWasFull = false;
5055
bool queueWasClosing = false;
56+
5157
for (int index = ARRAY_LENGTH - 1; index > -1 && !queueWasClosing; index--) {
5258
napi_status status = napi_generic_failure;
59+
5360
auto callback = [](Env env, Function jsCallback, int* data) {
5461
jsCallback.Call({Number::New(env, *data)});
5562
};
5663

64+
auto noArgCallback = [](Env env, Function jsCallback) {
65+
jsCallback.Call({Number::New(env, 42)});
66+
};
67+
5768
switch (info->type) {
5869
case ThreadSafeFunctionInfo::DEFAULT:
5970
status = s_tsfn.BlockingCall();
@@ -64,9 +75,17 @@ static void DataSourceThread() {
6475
case ThreadSafeFunctionInfo::NON_BLOCKING:
6576
status = s_tsfn.NonBlockingCall(&ints[index], callback);
6677
break;
78+
case ThreadSafeFunctionInfo::NON_BLOCKING_DEFAULT:
79+
status = s_tsfn.NonBlockingCall();
80+
break;
81+
82+
case ThreadSafeFunctionInfo::NON_BLOCKING_SINGLE_ARG:
83+
status = s_tsfn.NonBlockingCall(noArgCallback);
84+
break;
6785
}
6886

69-
if (info->abort && info->type != ThreadSafeFunctionInfo::NON_BLOCKING) {
87+
if (info->abort && (info->type == ThreadSafeFunctionInfo::BLOCKING ||
88+
info->type == ThreadSafeFunctionInfo::DEFAULT)) {
7089
// Let's make this thread really busy to give the main thread a chance to
7190
// abort / close.
7291
std::unique_lock<std::mutex> lk(info->protect);
@@ -176,6 +195,16 @@ static Value StartThreadNoNative(const CallbackInfo& info) {
176195
return StartThreadInternal(info, ThreadSafeFunctionInfo::DEFAULT);
177196
}
178197

198+
static Value StartThreadNonblockingNoNative(const CallbackInfo& info) {
199+
return StartThreadInternal(info,
200+
ThreadSafeFunctionInfo::NON_BLOCKING_DEFAULT);
201+
}
202+
203+
static Value StartThreadNonBlockingSingleArg(const CallbackInfo& info) {
204+
return StartThreadInternal(info,
205+
ThreadSafeFunctionInfo::NON_BLOCKING_SINGLE_ARG);
206+
}
207+
179208
Object InitThreadSafeFunction(Env env) {
180209
for (size_t index = 0; index < ARRAY_LENGTH; index++) {
181210
ints[index] = index;
@@ -186,8 +215,12 @@ Object InitThreadSafeFunction(Env env) {
186215
exports["MAX_QUEUE_SIZE"] = Number::New(env, MAX_QUEUE_SIZE);
187216
exports["startThread"] = Function::New(env, StartThread);
188217
exports["startThreadNoNative"] = Function::New(env, StartThreadNoNative);
218+
exports["startThreadNonblockingNoNative"] =
219+
Function::New(env, StartThreadNonblockingNoNative);
189220
exports["startThreadNonblocking"] =
190221
Function::New(env, StartThreadNonblocking);
222+
exports["startThreadNonblockSingleArg"] =
223+
Function::New(env, StartThreadNonBlockingSingleArg);
191224
exports["stopThread"] = Function::New(env, StopThread);
192225
exports["release"] = Function::New(env, Release);
193226

test/threadsafe_function/threadsafe_function.js

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const common = require('../common');
55

66
module.exports = common.runTest(test);
77

8+
// Main test body
89
async function test (binding) {
910
const expectedArray = (function (arrayLength) {
1011
const result = [];
@@ -14,6 +15,8 @@ async function test (binding) {
1415
return result;
1516
})(binding.threadsafe_function.ARRAY_LENGTH);
1617

18+
const expectedDefaultArray = Array.from({ length: binding.threadsafe_function.ARRAY_LENGTH }, (_, i) => 42);
19+
1720
function testWithJSMarshaller ({
1821
threadStarter,
1922
quitAfter,
@@ -31,7 +34,7 @@ async function test (binding) {
3134
}), !!abort);
3235
}
3336
}, !!abort, !!launchSecondary, maxQueueSize);
34-
if (threadStarter === 'startThreadNonblocking') {
37+
if ((threadStarter === 'startThreadNonblocking' || threadStarter === 'startThreadNonblockSingleArg')) {
3538
// Let's make this thread really busy for a short while to ensure that
3639
// the queue fills and the thread receives a napi_queue_full.
3740
const start = Date.now();
@@ -40,23 +43,28 @@ async function test (binding) {
4043
});
4144
}
4245

43-
await new Promise(function testWithoutJSMarshaller (resolve) {
44-
let callCount = 0;
45-
binding.threadsafe_function.startThreadNoNative(function testCallback () {
46-
callCount++;
46+
function testWithoutJSMarshallers (nativeFunction) {
47+
return new Promise((resolve) => {
48+
let callCount = 0;
49+
nativeFunction(function testCallback () {
50+
callCount++;
51+
52+
// The default call-into-JS implementation passes no arguments.
53+
assert.strictEqual(arguments.length, 0);
54+
if (callCount === binding.threadsafe_function.ARRAY_LENGTH) {
55+
setImmediate(() => {
56+
binding.threadsafe_function.stopThread(common.mustCall(() => {
57+
resolve();
58+
}), false);
59+
});
60+
}
61+
}, false /* abort */, false /* launchSecondary */,
62+
binding.threadsafe_function.MAX_QUEUE_SIZE);
63+
});
64+
}
4765

48-
// The default call-into-JS implementation passes no arguments.
49-
assert.strictEqual(arguments.length, 0);
50-
if (callCount === binding.threadsafe_function.ARRAY_LENGTH) {
51-
setImmediate(() => {
52-
binding.threadsafe_function.stopThread(common.mustCall(() => {
53-
resolve();
54-
}), false);
55-
});
56-
}
57-
}, false /* abort */, false /* launchSecondary */,
58-
binding.threadsafe_function.MAX_QUEUE_SIZE);
59-
});
66+
await testWithoutJSMarshallers(binding.threadsafe_function.startThreadNoNative);
67+
await testWithoutJSMarshallers(binding.threadsafe_function.startThreadNonblockingNoNative);
6068

6169
// Start the thread in blocking mode, and assert that all values are passed.
6270
// Quit after it's done.
@@ -124,6 +132,15 @@ async function test (binding) {
124132
expectedArray
125133
);
126134

135+
assert.deepStrictEqual(
136+
await testWithJSMarshaller({
137+
threadStarter: 'startThreadNonblockSingleArg',
138+
maxQueueSize: binding.threadsafe_function.MAX_QUEUE_SIZE,
139+
quitAfter: 1
140+
}),
141+
expectedDefaultArray
142+
);
143+
127144
// Start the thread in blocking mode, and assert that all values are passed.
128145
// Quit early, but let the thread finish. Launch a secondary thread to test
129146
// the reference counter incrementing functionality.
@@ -150,6 +167,16 @@ async function test (binding) {
150167
expectedArray
151168
);
152169

170+
assert.deepStrictEqual(
171+
await testWithJSMarshaller({
172+
threadStarter: 'startThreadNonblockSingleArg',
173+
maxQueueSize: binding.threadsafe_function.MAX_QUEUE_SIZE,
174+
quitAfter: 1,
175+
launchSecondary: true
176+
}),
177+
expectedDefaultArray
178+
);
179+
153180
// Start the thread in blocking mode, and assert that it could not finish.
154181
// Quit early by aborting.
155182
assert.strictEqual(
@@ -185,4 +212,14 @@ async function test (binding) {
185212
})).indexOf(0),
186213
-1
187214
);
215+
216+
assert.strictEqual(
217+
(await testWithJSMarshaller({
218+
threadStarter: 'startThreadNonblockSingleArg',
219+
quitAfter: 1,
220+
maxQueueSize: binding.threadsafe_function.MAX_QUEUE_SIZE,
221+
abort: true
222+
})).indexOf(0),
223+
-1
224+
);
188225
}

0 commit comments

Comments
 (0)