|  | 
|  | 1 | +'use strict'; | 
|  | 2 | + | 
|  | 3 | +const common = require('../../common'); | 
|  | 4 | +const assert = require('assert'); | 
|  | 5 | +const domain = require('domain'); | 
|  | 6 | +const binding = require('./build/Release/binding'); | 
|  | 7 | +const makeCallback = binding.makeCallback; | 
|  | 8 | + | 
|  | 9 | +// Make sure this is run in the future. | 
|  | 10 | +const mustCallCheckDomains = common.mustCall(checkDomains); | 
|  | 11 | + | 
|  | 12 | + | 
|  | 13 | +// Make sure that using MakeCallback allows the error to propagate. | 
|  | 14 | +assert.throws(function() { | 
|  | 15 | +  makeCallback({}, function() { | 
|  | 16 | +    throw new Error('hi from domain error'); | 
|  | 17 | +  }); | 
|  | 18 | +}); | 
|  | 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_arr = []; | 
|  | 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_arr.push(7); | 
|  | 33 | +  })); | 
|  | 34 | + | 
|  | 35 | +  // The nextTick should run after all immediately invoked calls. | 
|  | 36 | +  process.nextTick(common.mustCall(function() { | 
|  | 37 | +    results_arr.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_arr.push(6); | 
|  | 44 | +    })); | 
|  | 45 | + | 
|  | 46 | +    makeCallback({}, common.mustCall(function() { | 
|  | 47 | +      results_arr.push(4); | 
|  | 48 | +    })); | 
|  | 49 | + | 
|  | 50 | +    results_arr.push(5); | 
|  | 51 | +  })); | 
|  | 52 | + | 
|  | 53 | +  results_arr.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_arr.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_arr.push(2); | 
|  | 66 | + | 
|  | 67 | +  setImmediate(common.mustCall(function() { | 
|  | 68 | +    for (var i = 0; i < results_arr.length; i++) { | 
|  | 69 | +      assert.equal(results_arr[i], | 
|  | 70 | +                   i, | 
|  | 71 | +                   `verifyExecutionOrder(${arg}) results: ${results_arr}`); | 
|  | 72 | +    } | 
|  | 73 | +    if (arg === 1) { | 
|  | 74 | +      // The tests are first run on bootstrap during LoadEnvironment() in | 
|  | 75 | +      // src/node.cc. Now run the tests through node::MakeCallback(). | 
|  | 76 | +      setImmediate(function() { | 
|  | 77 | +        makeCallback({}, common.mustCall(function() { | 
|  | 78 | +          verifyExecutionOrder(2); | 
|  | 79 | +        })); | 
|  | 80 | +      }); | 
|  | 81 | +    } else if (arg === 2) { | 
|  | 82 | +      // setTimeout runs via the TimerWrap, which runs through | 
|  | 83 | +      // AsyncWrap::MakeCallback(). Make sure there are no conflicts using | 
|  | 84 | +      // node::MakeCallback() within it. | 
|  | 85 | +      setTimeout(common.mustCall(function() { | 
|  | 86 | +        verifyExecutionOrder(3); | 
|  | 87 | +      }), 10); | 
|  | 88 | +    } else if (arg === 3) { | 
|  | 89 | +      mustCallCheckDomains(); | 
|  | 90 | +    } else { | 
|  | 91 | +      throw new Error('UNREACHABLE'); | 
|  | 92 | +    } | 
|  | 93 | +  })); | 
|  | 94 | +}(1)); | 
|  | 95 | + | 
|  | 96 | + | 
|  | 97 | +function checkDomains() { | 
|  | 98 | +  // Check that domains are properly entered/exited when called in multiple | 
|  | 99 | +  // levels from both node::MakeCallback() and AsyncWrap::MakeCallback | 
|  | 100 | +  setImmediate(common.mustCall(function() { | 
|  | 101 | +    const d1 = domain.create(); | 
|  | 102 | +    const d2 = domain.create(); | 
|  | 103 | +    const d3 = domain.create(); | 
|  | 104 | + | 
|  | 105 | +    makeCallback({ domain: d1 }, common.mustCall(function() { | 
|  | 106 | +      assert.equal(d1, process.domain); | 
|  | 107 | +      makeCallback({ domain: d2 }, common.mustCall(function() { | 
|  | 108 | +        assert.equal(d2, process.domain); | 
|  | 109 | +        makeCallback({ domain: d3 }, common.mustCall(function() { | 
|  | 110 | +          assert.equal(d3, process.domain); | 
|  | 111 | +        })); | 
|  | 112 | +        assert.equal(d2, process.domain); | 
|  | 113 | +      })); | 
|  | 114 | +      assert.equal(d1, process.domain); | 
|  | 115 | +    })); | 
|  | 116 | +  })); | 
|  | 117 | + | 
|  | 118 | +  setTimeout(common.mustCall(function() { | 
|  | 119 | +    const d1 = domain.create(); | 
|  | 120 | +    const d2 = domain.create(); | 
|  | 121 | +    const d3 = domain.create(); | 
|  | 122 | + | 
|  | 123 | +    makeCallback({ domain: d1 }, common.mustCall(function() { | 
|  | 124 | +      assert.equal(d1, process.domain); | 
|  | 125 | +      makeCallback({ domain: d2 }, common.mustCall(function() { | 
|  | 126 | +        assert.equal(d2, process.domain); | 
|  | 127 | +        makeCallback({ domain: d3 }, common.mustCall(function() { | 
|  | 128 | +          assert.equal(d3, process.domain); | 
|  | 129 | +        })); | 
|  | 130 | +        assert.equal(d2, process.domain); | 
|  | 131 | +      })); | 
|  | 132 | +      assert.equal(d1, process.domain); | 
|  | 133 | +    })); | 
|  | 134 | +  }), 1); | 
|  | 135 | + | 
|  | 136 | +  // Make sure nextTick, setImmediate and setTimeout can all recover properly | 
|  | 137 | +  // after a thrown makeCallback call. | 
|  | 138 | +  process.nextTick(common.mustCall(function() { | 
|  | 139 | +    const d = domain.create(); | 
|  | 140 | +    d.on('error', common.mustCall(function(e) { | 
|  | 141 | +      assert.equal(e.message, 'throw from domain 3'); | 
|  | 142 | +    })); | 
|  | 143 | +    makeCallback({ domain: d }, function() { | 
|  | 144 | +      throw new Error('throw from domain 3'); | 
|  | 145 | +    }); | 
|  | 146 | +    throw new Error('UNREACHABLE'); | 
|  | 147 | +  })); | 
|  | 148 | + | 
|  | 149 | +  setImmediate(common.mustCall(function() { | 
|  | 150 | +    const d = domain.create(); | 
|  | 151 | +    d.on('error', common.mustCall(function(e) { | 
|  | 152 | +      assert.equal(e.message, 'throw from domain 2'); | 
|  | 153 | +    })); | 
|  | 154 | +    makeCallback({ domain: d }, function() { | 
|  | 155 | +      throw new Error('throw from domain 2'); | 
|  | 156 | +    }); | 
|  | 157 | +    throw new Error('UNREACHABLE'); | 
|  | 158 | +  })); | 
|  | 159 | + | 
|  | 160 | +  setTimeout(common.mustCall(function() { | 
|  | 161 | +    const d = domain.create(); | 
|  | 162 | +    d.on('error', common.mustCall(function(e) { | 
|  | 163 | +      assert.equal(e.message, 'throw from domain 1'); | 
|  | 164 | +    })); | 
|  | 165 | +    makeCallback({ domain: d }, function() { | 
|  | 166 | +      throw new Error('throw from domain 1'); | 
|  | 167 | +    }); | 
|  | 168 | +    throw new Error('UNREACHABLE'); | 
|  | 169 | +  })); | 
|  | 170 | +} | 
0 commit comments