• 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}) {
41  return new Promise((resolve) => {
42    const array = [];
43    binding[threadStarter](function testCallback(value) {
44      array.push(value);
45      if (array.length === quitAfter) {
46        setImmediate(() => {
47          binding.StopThread(common.mustCall(() => {
48            resolve(array);
49          }), !!abort);
50        });
51      }
52    }, !!abort, !!launchSecondary, maxQueueSize);
53    if (threadStarter === 'StartThreadNonblocking') {
54      // Let's make this thread really busy for a short while to ensure that
55      // the queue fills and the thread receives a napi_queue_full.
56      const start = Date.now();
57      while (Date.now() - start < 200);
58    }
59  });
60}
61
62function testUnref(queueSize) {
63  return new Promise((resolve, reject) => {
64    let output = '';
65    const child = fork(__filename, ['child', queueSize], {
66      stdio: [process.stdin, 'pipe', process.stderr, 'ipc']
67    });
68    child.on('close', (code) => {
69      if (code === 0) {
70        resolve(output.match(/\S+/g));
71      } else {
72        reject(new Error('Child process died with code ' + code));
73      }
74    });
75    child.stdout.on('data', (data) => (output += data.toString()));
76  })
77  .then((result) => assert.strictEqual(result.indexOf(0), -1));
78}
79
80new Promise(function testWithoutJSMarshaller(resolve) {
81  let callCount = 0;
82  binding.StartThreadNoNative(function testCallback() {
83    callCount++;
84
85    // The default call-into-JS implementation passes no arguments.
86    assert.strictEqual(arguments.length, 0);
87    if (callCount === binding.ARRAY_LENGTH) {
88      setImmediate(() => {
89        binding.StopThread(common.mustCall(() => {
90          resolve();
91        }), false);
92      });
93    }
94  }, false /* abort */, false /* launchSecondary */, binding.MAX_QUEUE_SIZE);
95})
96
97// Start the thread in blocking mode, and assert that all values are passed.
98// Quit after it's done.
99.then(() => testWithJSMarshaller({
100  threadStarter: 'StartThread',
101  maxQueueSize: binding.MAX_QUEUE_SIZE,
102  quitAfter: binding.ARRAY_LENGTH
103}))
104.then((result) => assert.deepStrictEqual(result, expectedArray))
105
106// Start the thread in blocking mode, and assert that all values are passed.
107// Quit after it's done.
108// Doesn't pass the callback js function to napi_create_threadsafe_function.
109// Instead, use an alternative reference to get js function called.
110.then(() => testWithJSMarshaller({
111  threadStarter: 'StartThreadNoJsFunc',
112  maxQueueSize: binding.MAX_QUEUE_SIZE,
113  quitAfter: binding.ARRAY_LENGTH
114}))
115.then((result) => assert.deepStrictEqual(result, expectedArray))
116
117// Start the thread in blocking mode with an infinite queue, and assert that all
118// values are passed. Quit after it's done.
119.then(() => testWithJSMarshaller({
120  threadStarter: 'StartThread',
121  maxQueueSize: 0,
122  quitAfter: binding.ARRAY_LENGTH
123}))
124.then((result) => assert.deepStrictEqual(result, expectedArray))
125
126// Start the thread in non-blocking mode, and assert that all values are passed.
127// Quit after it's done.
128.then(() => testWithJSMarshaller({
129  threadStarter: 'StartThreadNonblocking',
130  maxQueueSize: binding.MAX_QUEUE_SIZE,
131  quitAfter: binding.ARRAY_LENGTH
132}))
133.then((result) => assert.deepStrictEqual(result, expectedArray))
134
135// Start the thread in blocking mode, and assert that all values are passed.
136// Quit early, but let the thread finish.
137.then(() => testWithJSMarshaller({
138  threadStarter: 'StartThread',
139  maxQueueSize: binding.MAX_QUEUE_SIZE,
140  quitAfter: 1
141}))
142.then((result) => assert.deepStrictEqual(result, expectedArray))
143
144// Start the thread in blocking mode with an infinite queue, and assert that all
145// values are passed. Quit early, but let the thread finish.
146.then(() => testWithJSMarshaller({
147  threadStarter: 'StartThread',
148  maxQueueSize: 0,
149  quitAfter: 1
150}))
151.then((result) => assert.deepStrictEqual(result, expectedArray))
152
153// Start the thread in non-blocking mode, and assert that all values are passed.
154// Quit early, but let the thread finish.
155.then(() => testWithJSMarshaller({
156  threadStarter: 'StartThreadNonblocking',
157  maxQueueSize: binding.MAX_QUEUE_SIZE,
158  quitAfter: 1
159}))
160.then((result) => assert.deepStrictEqual(result, expectedArray))
161
162// Start the thread in blocking mode, and assert that all values are passed.
163// Quit early, but let the thread finish. Launch a secondary thread to test the
164// reference counter incrementing functionality.
165.then(() => testWithJSMarshaller({
166  threadStarter: 'StartThread',
167  quitAfter: 1,
168  maxQueueSize: binding.MAX_QUEUE_SIZE,
169  launchSecondary: true
170}))
171.then((result) => assert.deepStrictEqual(result, expectedArray))
172
173// Start the thread in non-blocking mode, and assert that all values are passed.
174// Quit early, but let the thread finish. Launch a secondary thread to test the
175// reference counter incrementing functionality.
176.then(() => testWithJSMarshaller({
177  threadStarter: 'StartThreadNonblocking',
178  quitAfter: 1,
179  maxQueueSize: binding.MAX_QUEUE_SIZE,
180  launchSecondary: true
181}))
182.then((result) => assert.deepStrictEqual(result, expectedArray))
183
184// Start the thread in blocking mode, and assert that it could not finish.
185// Quit early by aborting.
186.then(() => testWithJSMarshaller({
187  threadStarter: 'StartThread',
188  quitAfter: 1,
189  maxQueueSize: binding.MAX_QUEUE_SIZE,
190  abort: true
191}))
192.then((result) => assert.strictEqual(result.indexOf(0), -1))
193
194// Start the thread in blocking mode with an infinite queue, and assert that it
195// could not finish. Quit early by aborting.
196.then(() => testWithJSMarshaller({
197  threadStarter: 'StartThread',
198  quitAfter: 1,
199  maxQueueSize: 0,
200  abort: true
201}))
202.then((result) => assert.strictEqual(result.indexOf(0), -1))
203
204// Start the thread in non-blocking mode, and assert that it could not finish.
205// Quit early and aborting.
206.then(() => testWithJSMarshaller({
207  threadStarter: 'StartThreadNonblocking',
208  quitAfter: 1,
209  maxQueueSize: binding.MAX_QUEUE_SIZE,
210  abort: true
211}))
212.then((result) => assert.strictEqual(result.indexOf(0), -1))
213
214// Make sure that threadsafe function isn't stalled when we hit
215// `kMaxIterationCount` in `src/node_api.cc`
216.then(() => testWithJSMarshaller({
217  threadStarter: 'StartThreadNonblocking',
218  maxQueueSize: binding.ARRAY_LENGTH >>> 1,
219  quitAfter: binding.ARRAY_LENGTH
220}))
221.then((result) => assert.deepStrictEqual(result, expectedArray))
222
223// Start a child process to test rapid teardown
224.then(() => testUnref(binding.MAX_QUEUE_SIZE))
225
226// Start a child process with an infinite queue to test rapid teardown
227.then(() => testUnref(0));
228