1'use strict'; 2 3const common = require('../../common'); 4const assert = require('assert'); 5const binding = require(`./build/${common.buildType}/binding`); 6const { fork } = require('child_process'); 7const expectedArray = (function(arrayLength) { 8 const result = []; 9 for (let index = 0; index < arrayLength; index++) { 10 result.push(arrayLength - 1 - index); 11 } 12 return result; 13})(binding.ARRAY_LENGTH); 14 15// Handle the rapid teardown test case as the child process. We unref the 16// thread-safe function after we have received two values. This causes the 17// process to exit and the environment cleanup handler to be invoked. 18if (process.argv[2] === 'child') { 19 let callCount = 0; 20 binding.StartThread((value) => { 21 callCount++; 22 console.log(value); 23 if (callCount === 2) { 24 binding.Unref(); 25 } 26 }, false /* abort */, true /* launchSecondary */, +process.argv[3]); 27 28 // Release the thread-safe function from the main thread so that it may be 29 // torn down via the environment cleanup handler. 30 binding.Release(); 31 return; 32} 33 34function testWithJSMarshaller({ 35 threadStarter, 36 quitAfter, 37 abort, 38 maxQueueSize, 39 launchSecondary }) { 40 return new Promise((resolve) => { 41 const array = []; 42 binding[threadStarter](function testCallback(value) { 43 array.push(value); 44 if (array.length === quitAfter) { 45 setImmediate(() => { 46 binding.StopThread(common.mustCall(() => { 47 resolve(array); 48 }), !!abort); 49 }); 50 } 51 }, !!abort, !!launchSecondary, maxQueueSize); 52 if (threadStarter === 'StartThreadNonblocking') { 53 // Let's make this thread really busy for a short while to ensure that 54 // the queue fills and the thread receives a napi_queue_full. 55 const start = Date.now(); 56 while (Date.now() - start < 200); 57 } 58 }); 59} 60 61function testUnref(queueSize) { 62 return new Promise((resolve, reject) => { 63 let output = ''; 64 const child = fork(__filename, ['child', queueSize], { 65 stdio: [process.stdin, 'pipe', process.stderr, 'ipc'] 66 }); 67 child.on('close', (code) => { 68 if (code === 0) { 69 resolve(output.match(/\S+/g)); 70 } else { 71 reject(new Error('Child process died with code ' + code)); 72 } 73 }); 74 child.stdout.on('data', (data) => (output += data.toString())); 75 }) 76 .then((result) => assert.strictEqual(result.indexOf(0), -1)); 77} 78 79new Promise(function testWithoutJSMarshaller(resolve) { 80 let callCount = 0; 81 binding.StartThreadNoNative(function testCallback() { 82 callCount++; 83 84 // The default call-into-JS implementation passes no arguments. 85 assert.strictEqual(arguments.length, 0); 86 if (callCount === binding.ARRAY_LENGTH) { 87 setImmediate(() => { 88 binding.StopThread(common.mustCall(() => { 89 resolve(); 90 }), false); 91 }); 92 } 93 }, false /* abort */, false /* launchSecondary */, binding.MAX_QUEUE_SIZE); 94}) 95 96// Start the thread in blocking mode, and assert that all values are passed. 97// Quit after it's done. 98.then(() => testWithJSMarshaller({ 99 threadStarter: 'StartThread', 100 maxQueueSize: binding.MAX_QUEUE_SIZE, 101 quitAfter: binding.ARRAY_LENGTH 102})) 103.then((result) => assert.deepStrictEqual(result, expectedArray)) 104 105// Start the thread in blocking mode, and assert that all values are passed. 106// Quit after it's done. 107// Doesn't pass the callback js function to napi_create_threadsafe_function. 108// Instead, use an alternative reference to get js function called. 109.then(() => testWithJSMarshaller({ 110 threadStarter: 'StartThreadNoJsFunc', 111 maxQueueSize: binding.MAX_QUEUE_SIZE, 112 quitAfter: binding.ARRAY_LENGTH 113})) 114.then((result) => assert.deepStrictEqual(result, expectedArray)) 115 116// Start the thread in blocking mode with an infinite queue, and assert that all 117// values are passed. Quit after it's done. 118.then(() => testWithJSMarshaller({ 119 threadStarter: 'StartThread', 120 maxQueueSize: 0, 121 quitAfter: binding.ARRAY_LENGTH 122})) 123.then((result) => assert.deepStrictEqual(result, expectedArray)) 124 125// Start the thread in non-blocking mode, and assert that all values are passed. 126// Quit after it's done. 127.then(() => testWithJSMarshaller({ 128 threadStarter: 'StartThreadNonblocking', 129 maxQueueSize: binding.MAX_QUEUE_SIZE, 130 quitAfter: binding.ARRAY_LENGTH 131})) 132.then((result) => assert.deepStrictEqual(result, expectedArray)) 133 134// Start the thread in blocking mode, and assert that all values are passed. 135// Quit early, but let the thread finish. 136.then(() => testWithJSMarshaller({ 137 threadStarter: 'StartThread', 138 maxQueueSize: binding.MAX_QUEUE_SIZE, 139 quitAfter: 1 140})) 141.then((result) => assert.deepStrictEqual(result, expectedArray)) 142 143// Start the thread in blocking mode with an infinite queue, and assert that all 144// values are passed. Quit early, but let the thread finish. 145.then(() => testWithJSMarshaller({ 146 threadStarter: 'StartThread', 147 maxQueueSize: 0, 148 quitAfter: 1 149})) 150.then((result) => assert.deepStrictEqual(result, expectedArray)) 151 152// Start the thread in non-blocking mode, and assert that all values are passed. 153// Quit early, but let the thread finish. 154.then(() => testWithJSMarshaller({ 155 threadStarter: 'StartThreadNonblocking', 156 maxQueueSize: binding.MAX_QUEUE_SIZE, 157 quitAfter: 1 158})) 159.then((result) => assert.deepStrictEqual(result, expectedArray)) 160 161// Start the thread in blocking mode, and assert that all values are passed. 162// Quit early, but let the thread finish. Launch a secondary thread to test the 163// reference counter incrementing functionality. 164.then(() => testWithJSMarshaller({ 165 threadStarter: 'StartThread', 166 quitAfter: 1, 167 maxQueueSize: binding.MAX_QUEUE_SIZE, 168 launchSecondary: true 169})) 170.then((result) => assert.deepStrictEqual(result, expectedArray)) 171 172// Start the thread in non-blocking mode, and assert that all values are passed. 173// Quit early, but let the thread finish. Launch a secondary thread to test the 174// reference counter incrementing functionality. 175.then(() => testWithJSMarshaller({ 176 threadStarter: 'StartThreadNonblocking', 177 quitAfter: 1, 178 maxQueueSize: binding.MAX_QUEUE_SIZE, 179 launchSecondary: true 180})) 181.then((result) => assert.deepStrictEqual(result, expectedArray)) 182 183// Start the thread in blocking mode, and assert that it could not finish. 184// Quit early by aborting. 185.then(() => testWithJSMarshaller({ 186 threadStarter: 'StartThread', 187 quitAfter: 1, 188 maxQueueSize: binding.MAX_QUEUE_SIZE, 189 abort: true 190})) 191.then((result) => assert.strictEqual(result.indexOf(0), -1)) 192 193// Start the thread in blocking mode with an infinite queue, and assert that it 194// could not finish. Quit early by aborting. 195.then(() => testWithJSMarshaller({ 196 threadStarter: 'StartThread', 197 quitAfter: 1, 198 maxQueueSize: 0, 199 abort: true 200})) 201.then((result) => assert.strictEqual(result.indexOf(0), -1)) 202 203// Start the thread in non-blocking mode, and assert that it could not finish. 204// Quit early and aborting. 205.then(() => testWithJSMarshaller({ 206 threadStarter: 'StartThreadNonblocking', 207 quitAfter: 1, 208 maxQueueSize: binding.MAX_QUEUE_SIZE, 209 abort: true 210})) 211.then((result) => assert.strictEqual(result.indexOf(0), -1)) 212 213// Start a child process to test rapid teardown 214.then(() => testUnref(binding.MAX_QUEUE_SIZE)) 215 216// Start a child process with an infinite queue to test rapid teardown 217.then(() => testUnref(0)); 218