• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../../common');
4const assert = require('assert');
5const domain = require('domain');
6const binding = require(`./build/${common.buildType}/binding`);
7const makeCallback = binding.makeCallback;
8
9// Make sure this is run in the future.
10const mustCallCheckDomains = common.mustCall(checkDomains);
11
12// Make sure that using MakeCallback allows the error to propagate.
13assert.throws(() => {
14  makeCallback({}, () => {
15    throw new Error('hi from domain error');
16  });
17}, /^Error: hi from domain error$/);
18
19
20// Check the execution order of the nextTickQueue and MicrotaskQueue in
21// relation to running multiple MakeCallback's from bootstrap,
22// node::MakeCallback() and node::AsyncWrap::MakeCallback().
23// TODO(trevnorris): Is there a way to verify this is being run during
24// bootstrap?
25(function verifyExecutionOrder(arg) {
26  const results = [];
27
28  // Processing of the MicrotaskQueue is manually handled by node. They are not
29  // processed until after the nextTickQueue has been processed.
30  Promise.resolve(1).then(common.mustCall(() => {
31    results.push(7);
32  }));
33
34  // The nextTick should run after all immediately invoked calls.
35  process.nextTick(common.mustCall(() => {
36    results.push(3);
37
38    // Run same test again but while processing the nextTickQueue to make sure
39    // the following MakeCallback call breaks in the middle of processing the
40    // queue and allows the script to run normally.
41    process.nextTick(common.mustCall(() => {
42      results.push(6);
43    }));
44
45    makeCallback({}, common.mustCall(() => {
46      results.push(4);
47    }));
48
49    results.push(5);
50  }));
51
52  results.push(0);
53
54  // MakeCallback is calling the function immediately, but should then detect
55  // that a script is already in the middle of execution and return before
56  // either the nextTickQueue or MicrotaskQueue are processed.
57  makeCallback({}, common.mustCall(() => {
58    results.push(1);
59  }));
60
61  // This should run before either the nextTickQueue or MicrotaskQueue are
62  // processed. Previously MakeCallback would not detect this circumstance
63  // and process them immediately.
64  results.push(2);
65
66  setImmediate(common.mustCall(() => {
67    for (let i = 0; i < results.length; i++) {
68      assert.strictEqual(results[i], i,
69                         `verifyExecutionOrder(${arg}) results: ${results}`);
70    }
71    if (arg === 1) {
72      // The tests are first run on bootstrap during LoadEnvironment() in
73      // src/node.cc. Now run the tests through node::MakeCallback().
74      setImmediate(() => {
75        makeCallback({}, common.mustCall(() => {
76          verifyExecutionOrder(2);
77        }));
78      });
79    } else if (arg === 2) {
80      // Make sure there are no conflicts using node::MakeCallback()
81      // within timers.
82      setTimeout(common.mustCall(() => {
83        verifyExecutionOrder(3);
84      }), 10);
85    } else if (arg === 3) {
86      mustCallCheckDomains();
87    } else {
88      throw new Error('UNREACHABLE');
89    }
90  }));
91}(1));
92
93
94function checkDomains() {
95  // Check that domains are properly entered/exited when called in multiple
96  // levels from both node::MakeCallback() and AsyncWrap::MakeCallback
97  setImmediate(common.mustCall(() => {
98    const d1 = domain.create();
99    const d2 = domain.create();
100    const d3 = domain.create();
101
102    makeCallback({ domain: d1 }, common.mustCall(() => {
103      assert.strictEqual(d1, process.domain);
104      makeCallback({ domain: d2 }, common.mustCall(() => {
105        assert.strictEqual(d2, process.domain);
106        makeCallback({ domain: d3 }, common.mustCall(() => {
107          assert.strictEqual(d3, process.domain);
108        }));
109        assert.strictEqual(d2, process.domain);
110      }));
111      assert.strictEqual(d1, process.domain);
112    }));
113  }));
114
115  setTimeout(common.mustCall(() => {
116    const d1 = domain.create();
117    const d2 = domain.create();
118    const d3 = domain.create();
119
120    makeCallback({ domain: d1 }, common.mustCall(() => {
121      assert.strictEqual(d1, process.domain);
122      makeCallback({ domain: d2 }, common.mustCall(() => {
123        assert.strictEqual(d2, process.domain);
124        makeCallback({ domain: d3 }, common.mustCall(() => {
125          assert.strictEqual(d3, process.domain);
126        }));
127        assert.strictEqual(d2, process.domain);
128      }));
129      assert.strictEqual(d1, process.domain);
130    }));
131  }), 1);
132
133  function testTimer(id) {
134    // Make sure nextTick, setImmediate and setTimeout can all recover properly
135    // after a thrown makeCallback call.
136    const d = domain.create();
137    d.on('error', common.mustCall((e) => {
138      assert.strictEqual(e.message, `throw from domain ${id}`);
139    }));
140    makeCallback({ domain: d }, () => {
141      throw new Error(`throw from domain ${id}`);
142    });
143    throw new Error('UNREACHABLE');
144  }
145
146  process.nextTick(common.mustCall(testTimer), 3);
147  setImmediate(common.mustCall(testTimer), 2);
148  setTimeout(common.mustCall(testTimer), 1, 1);
149}
150