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