-
Notifications
You must be signed in to change notification settings - Fork 31k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
events: remove domain handling from events to domain #17403
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,11 +31,6 @@ const EventEmitter = require('events'); | |
const errors = require('internal/errors'); | ||
const { createHook } = require('async_hooks'); | ||
|
||
// communicate with events module, but don't require that | ||
// module to have to load this one, since this module has | ||
// a few side effects. | ||
EventEmitter.usingDomains = true; | ||
|
||
// overwrite process.domain with a getter/setter that will allow for more | ||
// effective optimizations | ||
var _domain = [null]; | ||
|
@@ -387,3 +382,49 @@ 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() { | ||
this.domain = null; | ||
if (exports.active && !(this instanceof exports.Domain)) { | ||
this.domain = exports.active; | ||
} | ||
|
||
return eventInit.call(this); | ||
}; | ||
|
||
const eventEmit = EventEmitter.prototype.emit; | ||
EventEmitter.prototype.emit = function emit(...args) { | ||
const domain = this.domain; | ||
if (domain === null || domain === undefined || this === process) { | ||
return eventEmit.apply(this, args); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a PR introducing it there too, it's just currently blocked on some other stuff. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @apapirovski sounds good - although a little defensive (if someone overrides There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We definitely should. The usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course. But it's a step better because we're no longer relying on a method of a user provided function. At least Strictly speaking, using some sort of an internal way of invoking functions brings us closer to the spec too (e.g. for timers). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, to elaborate, this has become less of an issue lately but it was potentially even more problematic in the past where certain functions were called as |
||
} | ||
|
||
const type = args[0]; | ||
// edge case: if there is a domain and an existing non error object is given, | ||
// it should not be errorized | ||
// see test/parallel/test-event-emitter-no-error-provided-to-error-event.js | ||
if (type === 'error' && args.length > 1 && args[1] && | ||
!(args[1] instanceof Error)) { | ||
domain.emit('error', args[1]); | ||
return false; | ||
} | ||
|
||
domain.enter(); | ||
try { | ||
return eventEmit.apply(this, args); | ||
} catch (er) { | ||
if (typeof er === 'object' && er !== null) { | ||
er.domainEmitter = this; | ||
er.domain = domain; | ||
er.domainThrown = false; | ||
} | ||
domain.emit('error', er); | ||
return false; | ||
} finally { | ||
domain.exit(); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vdeturckheim Would you mind changing this to
emit()
and then usingarguments
everywhere? I realize performance is not that big of a deal but this will actually make a substantial difference.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@apapirovski are you sure it's faster? (In recent V8)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm certain because I checked it very recently.
...args
is only faster when arguments requires slicing or manually copying in situations likeemit(type, ...args)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@apapirovski very interesting can you share a benchmark showing that? I'm pretty sure
...args
is optimized.cc @bmeurer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been optimized but in practical usage it still seems to perform worse. I've run into this working on
nextTick
,events
andtimers
.The basic benchmark shows it being optimized. As soon as I've tried using it in practice where there's more than one layer to the usage, arguments seems to win out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw @benjamingr I decided to finally split test all the possible variations. Here are my findings:
is the fastest for that particular purpose, faster than using
arguments
(about 3-5% faster). But then there's:seems to depend on specific usage of
args
withinfn
. For example, I've found that insetTimeout
andsetImmediate
we incur about 20-25% performance penalty compared to using manual copying ofarguments
.On the flipside, it performs as expected within
EventEmitter.prototype.emit
... and is, in fact, yet again faster than usingarguments
. There definitely seem to be some edge cases around this behaviour that I don't quite understand.I don't know if this helps with your plan to bug V8 devs regarding this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible this might be related to whether
args
is being stored somewhere or just passed through to another function, or whetherargs
is accessed at all in the body?