Repository: node
Issue: error handler of same domain can be called several times when it throws
Labels: help wanted,domain

<!--
Thank you for reporting a possible bug in Node.js.

Please fill in as much of the template below as you can.

Version: output of `node -v`
Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
Subsystem: if known, please specify the affected core module name

If possible, please provide code that demonstrates the problem, keeping it as
simple and free of external dependencies as you can.
-->

* **Version**: Current tip of master (66f45e7e5b), but probably applies to all versions.
* **Platform**: All platforms.
* **Subsystem**: domain.

<!-- Please provide more details below this comment. -->

The following code:

```
'use strict';

const common = require('../common');
const domain = require('domain');
const http = require('http');

const server = http.createServer((req, res) => {
  res.end();
});

let numDomainErrorListenerCalls = 0;

function performHttpRequestWithDomain(cb) {
  const d = domain.create();
  d.run(() => {
    const req = http.get({
      host: '127.0.0.1', port: server.address().port
    }, (res) => {
      res.on('data', () => {});
      res.on('end', () => {
        throw new Error('bang');
      });
    });

    req.end();
  });

  d.on('error', (domainErr) => {
    console.log(++numDomainErrorListenerCalls);
    throw new Error('boom');
  });
}

server.listen(0, '127.0.0.1', () => {
  performHttpRequestWithDomain(common.mustCall(() => {
    server.close();
  }));
});
```

gives the following output:
```
$ ./node test/parallel/test-http-req-domain-stack.js 
1
2
/Users/jgilli/dev/node/test/parallel/test-http-req-domain-stack.js:30
    throw new Error('boom');
    ^

Error: boom
    at Domain.d.on (/Users/jgilli/dev/node/test/parallel/test-http-req-domain-stack.js:30:11)
    at Domain.emit (events.js:188:13)
    at Domain.EventEmitter.emit (domain.js:430:20)
    at Domain._errorHandler (domain.js:216:23)
    at Domain._errorHandler (domain.js:244:33)
    at Object.setUncaughtExceptionCaptureCallback (domain.js:132:29)
    at process._fatalException (internal/process/execution.js:102:29)
```

The uncaught exception is expected. What is not expected as far as I understand is for the same domain's error handler to run more than once.

I believe the original intention of the domain's implementation is to [pop the domains stack when a domain's error handler throw](https://github.com/nodejs/node/blob/66f45e7e5b7ef661678eb48bb630bb99acc7618b/lib/domain.js#L233-L240), so that the domain that handles that new error is the "parent" domain.

However, in the example above the same domain is pushed on the stack more than once. For instance, when an event is emitted from a nextTick callback, the same domain will be entered from [the nextTick callbacks scheduler](https://github.com/nodejs/node/blob/66f45e7e5b7ef661678eb48bb630bb99acc7618b/lib/internal/process/next_tick.js#L59) and then once again from the [event emitter](https://github.com/nodejs/node/blob/66f45e7e5b7ef661678eb48bb630bb99acc7618b/lib/domain.js#L449).

Pushing the same domain on the stack more than once makes sense so that the components that push a domain can pop it from the stack. However, I think we could probably replace [the call to pop the stack once](https://github.com/nodejs/node/blob/66f45e7e5b7ef661678eb48bb630bb99acc7618b/lib/domain.js#L239) in the domain error handling code to remove all consecutive instances of that domain instead.

@nodejs/domains Thoughts?