• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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