218 lines
7.1 KiB
JavaScript
218 lines
7.1 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const common = require('../../common');
|
||
|
const assert = require('assert');
|
||
|
const binding = require(`./build/${common.buildType}/binding`);
|
||
|
const { fork } = require('child_process');
|
||
|
const expectedArray = (function(arrayLength) {
|
||
|
const result = [];
|
||
|
for (let index = 0; index < arrayLength; index++) {
|
||
|
result.push(arrayLength - 1 - index);
|
||
|
}
|
||
|
return result;
|
||
|
})(binding.ARRAY_LENGTH);
|
||
|
|
||
|
// Handle the rapid teardown test case as the child process. We unref the
|
||
|
// thread-safe function after we have received two values. This causes the
|
||
|
// process to exit and the environment cleanup handler to be invoked.
|
||
|
if (process.argv[2] === 'child') {
|
||
|
let callCount = 0;
|
||
|
binding.StartThread((value) => {
|
||
|
callCount++;
|
||
|
console.log(value);
|
||
|
if (callCount === 2) {
|
||
|
binding.Unref();
|
||
|
}
|
||
|
}, false /* abort */, true /* launchSecondary */, +process.argv[3]);
|
||
|
|
||
|
// Release the thread-safe function from the main thread so that it may be
|
||
|
// torn down via the environment cleanup handler.
|
||
|
binding.Release();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
function testWithJSMarshaller({
|
||
|
threadStarter,
|
||
|
quitAfter,
|
||
|
abort,
|
||
|
maxQueueSize,
|
||
|
launchSecondary }) {
|
||
|
return new Promise((resolve) => {
|
||
|
const array = [];
|
||
|
binding[threadStarter](function testCallback(value) {
|
||
|
array.push(value);
|
||
|
if (array.length === quitAfter) {
|
||
|
setImmediate(() => {
|
||
|
binding.StopThread(common.mustCall(() => {
|
||
|
resolve(array);
|
||
|
}), !!abort);
|
||
|
});
|
||
|
}
|
||
|
}, !!abort, !!launchSecondary, maxQueueSize);
|
||
|
if (threadStarter === 'StartThreadNonblocking') {
|
||
|
// Let's make this thread really busy for a short while to ensure that
|
||
|
// the queue fills and the thread receives a napi_queue_full.
|
||
|
const start = Date.now();
|
||
|
while (Date.now() - start < 200);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function testUnref(queueSize) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
let output = '';
|
||
|
const child = fork(__filename, ['child', queueSize], {
|
||
|
stdio: [process.stdin, 'pipe', process.stderr, 'ipc']
|
||
|
});
|
||
|
child.on('close', (code) => {
|
||
|
if (code === 0) {
|
||
|
resolve(output.match(/\S+/g));
|
||
|
} else {
|
||
|
reject(new Error('Child process died with code ' + code));
|
||
|
}
|
||
|
});
|
||
|
child.stdout.on('data', (data) => (output += data.toString()));
|
||
|
})
|
||
|
.then((result) => assert.strictEqual(result.indexOf(0), -1));
|
||
|
}
|
||
|
|
||
|
new Promise(function testWithoutJSMarshaller(resolve) {
|
||
|
let callCount = 0;
|
||
|
binding.StartThreadNoNative(function testCallback() {
|
||
|
callCount++;
|
||
|
|
||
|
// The default call-into-JS implementation passes no arguments.
|
||
|
assert.strictEqual(arguments.length, 0);
|
||
|
if (callCount === binding.ARRAY_LENGTH) {
|
||
|
setImmediate(() => {
|
||
|
binding.StopThread(common.mustCall(() => {
|
||
|
resolve();
|
||
|
}), false);
|
||
|
});
|
||
|
}
|
||
|
}, false /* abort */, false /* launchSecondary */, binding.MAX_QUEUE_SIZE);
|
||
|
})
|
||
|
|
||
|
// Start the thread in blocking mode, and assert that all values are passed.
|
||
|
// Quit after it's done.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
quitAfter: binding.ARRAY_LENGTH
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in blocking mode, and assert that all values are passed.
|
||
|
// Quit after it's done.
|
||
|
// Doesn't pass the callback js function to napi_create_threadsafe_function.
|
||
|
// Instead, use an alternative reference to get js function called.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThreadNoJsFunc',
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
quitAfter: binding.ARRAY_LENGTH
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in blocking mode with an infinite queue, and assert that all
|
||
|
// values are passed. Quit after it's done.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
maxQueueSize: 0,
|
||
|
quitAfter: binding.ARRAY_LENGTH
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in non-blocking mode, and assert that all values are passed.
|
||
|
// Quit after it's done.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThreadNonblocking',
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
quitAfter: binding.ARRAY_LENGTH
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in blocking mode, and assert that all values are passed.
|
||
|
// Quit early, but let the thread finish.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
quitAfter: 1
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in blocking mode with an infinite queue, and assert that all
|
||
|
// values are passed. Quit early, but let the thread finish.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
maxQueueSize: 0,
|
||
|
quitAfter: 1
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in non-blocking mode, and assert that all values are passed.
|
||
|
// Quit early, but let the thread finish.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThreadNonblocking',
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
quitAfter: 1
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in blocking mode, and assert that all values are passed.
|
||
|
// Quit early, but let the thread finish. Launch a secondary thread to test the
|
||
|
// reference counter incrementing functionality.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
quitAfter: 1,
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
launchSecondary: true
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in non-blocking mode, and assert that all values are passed.
|
||
|
// Quit early, but let the thread finish. Launch a secondary thread to test the
|
||
|
// reference counter incrementing functionality.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThreadNonblocking',
|
||
|
quitAfter: 1,
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
launchSecondary: true
|
||
|
}))
|
||
|
.then((result) => assert.deepStrictEqual(result, expectedArray))
|
||
|
|
||
|
// Start the thread in blocking mode, and assert that it could not finish.
|
||
|
// Quit early by aborting.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
quitAfter: 1,
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
abort: true
|
||
|
}))
|
||
|
.then((result) => assert.strictEqual(result.indexOf(0), -1))
|
||
|
|
||
|
// Start the thread in blocking mode with an infinite queue, and assert that it
|
||
|
// could not finish. Quit early by aborting.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThread',
|
||
|
quitAfter: 1,
|
||
|
maxQueueSize: 0,
|
||
|
abort: true
|
||
|
}))
|
||
|
.then((result) => assert.strictEqual(result.indexOf(0), -1))
|
||
|
|
||
|
// Start the thread in non-blocking mode, and assert that it could not finish.
|
||
|
// Quit early and aborting.
|
||
|
.then(() => testWithJSMarshaller({
|
||
|
threadStarter: 'StartThreadNonblocking',
|
||
|
quitAfter: 1,
|
||
|
maxQueueSize: binding.MAX_QUEUE_SIZE,
|
||
|
abort: true
|
||
|
}))
|
||
|
.then((result) => assert.strictEqual(result.indexOf(0), -1))
|
||
|
|
||
|
// Start a child process to test rapid teardown
|
||
|
.then(() => testUnref(binding.MAX_QUEUE_SIZE))
|
||
|
|
||
|
// Start a child process with an infinite queue to test rapid teardown
|
||
|
.then(() => testUnref(0));
|