Skip to content

Commit

Permalink
fixup! domain: allow concurrent user-land impl
Browse files Browse the repository at this point in the history
  • Loading branch information
Trott committed Aug 7, 2020
1 parent 36811bc commit 9fb161c
Showing 1 changed file with 100 additions and 1 deletion.
101 changes: 100 additions & 1 deletion lib/domain.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const {
} = primordials;

const EventEmitter = require('events');
const { ERR_UNHANDLED_ERROR } = require('internal/errors').codes;
const { createHook } = require('async_hooks');
const { useDomainTrampoline } = require('internal/async_hooks');

Expand Down Expand Up @@ -128,7 +129,7 @@ function topLevelDomainCallback(cb, ...args) {

// It's possible to enter one domain while already inside another one. The stack
// is each entered domain.
const stack = process._domainStack;
let stack = [];
exports._stack = stack;
useDomainTrampoline(topLevelDomainCallback);

Expand Down Expand Up @@ -438,3 +439,101 @@ Domain.prototype.bind = function(cb) {

return runBound;
};

// Override EventEmitter methods to make it domain-aware.
EventEmitter.usingDomains = true;

const eventInit = EventEmitter.init;
EventEmitter.init = function() {
ObjectDefineProperty(this, 'domain', {
configurable: true,
enumerable: false,
value: null,
writable: true
});
if (exports.active && !(this instanceof exports.Domain)) {
this.domain = exports.active;
}

return eventInit.call(this);
};

const eventEmit = EventEmitter.prototype.emit;
EventEmitter.prototype.emit = function(...args) {
const domain = this.domain;

const type = args[0];
const shouldEmitError = type === 'error' &&
this.listenerCount(type) > 0;

// Just call original `emit` if current EE instance has `error`
// handler, there's no active domain or this is process
if (shouldEmitError || domain === null || domain === undefined ||
this === process) {
return ReflectApply(eventEmit, this, args);
}

if (type === 'error') {
const er = args.length > 1 && args[1] ?
args[1] : new ERR_UNHANDLED_ERROR();

if (typeof er === 'object') {
er.domainEmitter = this;
ObjectDefineProperty(er, 'domain', {
configurable: true,
enumerable: false,
value: domain,
writable: true
});
er.domainThrown = false;
}

// Remove the current domain (and its duplicates) from the domains stack and
// set the active domain to its parent (if any) so that the domain's error
// handler doesn't run in its own context. This prevents any event emitter
// created or any exception thrown in that error handler from recursively
// executing that error handler.
const origDomainsStack = stack.slice();
const origActiveDomain = process.domain;

// Travel the domains stack from top to bottom to find the first domain
// instance that is not a duplicate of the current active domain.
let idx = stack.length - 1;
while (idx > -1 && process.domain === stack[idx]) {
--idx;
}

// Change the stack to not contain the current active domain, and only the
// domains above it on the stack.
if (idx < 0) {
stack.length = 0;
} else {
stack.splice(idx + 1);
}

// Change the current active domain
if (stack.length > 0) {
exports.active = process.domain = stack[stack.length - 1];
} else {
exports.active = process.domain = null;
}

updateExceptionCapture();

domain.emit('error', er);

// Now that the domain's error handler has completed, restore the domains
// stack and the active domain to their original values.
exports._stack = stack = origDomainsStack;
exports.active = process.domain = origActiveDomain;
updateExceptionCapture();

return false;
}

domain.enter();
const ret = ReflectApply(eventEmit, this, args);
domain.exit();

return ret;
};

0 comments on commit 9fb161c

Please sign in to comment.