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