| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Sorry, something went wrong.
There was a problem hiding this comment.
Generally looks good. Couple of nits
Sorry, something went wrong.
| --> | ||
| - `eventName` {any} | ||
| * `eventName` {any} The name of the event. | ||
| * `unwrap` {boolean} When set to true, will return the original listeners |
There was a problem hiding this comment.
Not generally a fan of Boolean args in public APIs. Perhaps having a separate unwrappedListeners() method would work?
Sorry, something went wrong.
There was a problem hiding this comment.
I think it would need to be wrappedListeners or something similar. Will think on it.
Sorry, something went wrong.
There was a problem hiding this comment.
If you want an option argument, I'd prefer to be explicit and call it unwrapOnceListeners (which could also be a string or object instead of a boolean).
However, from a performance point of view new methods for separate input types should always be a good idea.
Sorry, something went wrong.
There was a problem hiding this comment.
This should be addressed now, instead we have a wrappedListeners method on EventEmitter.
Sorry, something went wrong.
| } else if (typeof list !== 'function') { | ||
| position = -1; | ||
| events[type] = undefined; | ||
| --this._eventsCount; |
There was a problem hiding this comment.
Having the -- at the start here just looks odd. Having it at the end may be more readable
Sorry, something went wrong.
There was a problem hiding this comment.
Yeah, I'm not sure which way to go here. It matches the rest of the events. Feels more confusing if we go back and forth throughout this code. Maybe just convert all the usage within this file to be postfix (where the result isn't being used)?
Sorry, something went wrong.
There was a problem hiding this comment.
I'm okay with having consistency in this file.
It probably looks a lot less weird to you if you're used to writing C++ code :)
Sorry, something went wrong.
|
Why the large eventNames() regression? |
Sorry, something went wrong.
|
@mscdex We have to iterate over _events now to find where listeners exist as opposed to just undefined. In practice, this seems less of an issue than the performance of on, removeListener and emit. Even packages that use eventNames (such as ultron) seem to benefit since they usually couple it with removeListener. |
Sorry, something went wrong.
|
@mscdex Refactored that method and now it iterates over event names without Reflect.ownKeys (using for..in & Object.getOwnPropertySymbols) which is mostly faster than it was previously, with the tiny exception being a really small list of events (less than 10) that includes Symbols. For most use cases there's now no regression. events/ee-event-names.js symbols="false" listeners=10 n=1000000 9.47 % *** 2.556083e-48 events/ee-event-names.js symbols="false" listeners=1 n=1000000 22.61 % *** 3.692565e-20 events/ee-event-names.js symbols="true" listeners=10 n=1000000 10.37 % *** 2.488306e-95 events/ee-event-names.js symbols="true" listeners=1 n=1000000 -25.36 % *** 2.084307e-58 |
Sorry, something went wrong.
| --> | ||
| - `eventName` {any} | ||
| * `eventName` {any} The name of the event. | ||
| * `unwrap` {boolean} When set to true, will return the original listeners |
There was a problem hiding this comment.
If you want an option argument, I'd prefer to be explicit and call it unwrapOnceListeners (which could also be a string or object instead of a boolean).
However, from a performance point of view new methods for separate input types should always be a good idea.
Sorry, something went wrong.
| } else if (typeof list !== 'function') { | ||
| position = -1; | ||
| events[type] = undefined; | ||
| --this._eventsCount; |
There was a problem hiding this comment.
I'm okay with having consistency in this file.
It probably looks a lot less weird to you if you're used to writing C++ code :)
Sorry, something went wrong.
There was a problem hiding this comment.
I'm still not sure what exactly shouldCatch entails. I'd like to get #17159 done at some point... maybe we can try to do that before this lands?
Sorry, something went wrong.
There was a problem hiding this comment.
This part is slightly different than the work within that PR. It's so we don't need ClearFatalExceptionHandlers which deep reaches into a bunch of JS code (including _events).
The part of this PR that would be made obsolete by #17159 is within lib/domain.js (& a bit within node.cc). I'm guessing this could land and then we can refactor later? Not sure where that work is at though so I'm happy to wait if it's close.
Edit: The main reason I say 'land this' is just because it's strictly better than what's there currently which requires actually inspecting the _events object and looking for error handlers, whereas now that responsibility is mostly in JS land and all we check for is a single flag. Far from ideal but a lot better.
Sorry, something went wrong.
At its call sites, `ClearFatalExceptionHandlers()` was used to make the process crash as soon as possible once an exception occurred, without giving JS land a chance to interfere. `ClearFatalExceptionHandlers()` awkwardly removed the current domain and any `uncaughtException` handlers, whereas a clearer way is to execute the relevant reporting (and `exit()`) code directly. Refs: nodejs#17159 Refs: nodejs#17324
At its call sites, `ClearFatalExceptionHandlers()` was used to make the process crash as soon as possible once an exception occurred, without giving JS land a chance to interfere. `ClearFatalExceptionHandlers()` awkwardly removed the current domain and any `uncaughtException` handlers, whereas a clearer way is to execute the relevant reporting (and `exit()`) code directly. PR-URL: #17333 Refs: #17159 Refs: #17324 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Andreas Madsen <amwebdk@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
|
@apapirovski Just merged the other 2 PRs :) |
Sorry, something went wrong.
If more than one handler is bound to an event type, avoid copying it on emit and instead set a flag that regulates whether to make a copy when a new event is added or removed. Do not use delete or Object.create(null) when removing listeners, instead set to undefined and modify other methods to account for this.
Refactor lib & src code to eliminate all deep reaches into the internal _events dictionary object, instead use available APIs and add an extra method to EventEmitter: wrappedListeners.
|
Updated. PTAL everyone. CI: https://ci.nodejs.org/job/node-test-pull-request/11804/ |
Sorry, something went wrong.
|
|
||
| for (var k = 0; k < listeners; k += 1) | ||
| if (listeners === 1) | ||
| n *= 5; |
There was a problem hiding this comment.
I don't think automagically adjusting the n value is a good idea.
Sorry, something went wrong.
There was a problem hiding this comment.
I mean, we don't have a ton of options here. The benchmark doesn't run long enough with 1 listener whereas setting a higher default n makes the other ones run way too long.
Sorry, something went wrong.
There was a problem hiding this comment.
That's just a general benchmarking problem, just like any particular n value might cause things to run too fast or too slow on someone else's machine. At some point you will just need to supply your own set of values for each parameter if you have combinations that vary in speed that much.
Sorry, something went wrong.
| - `eventName` {any} | ||
|
|
||
| Returns a copy of the array of listeners for the event named `eventName`, | ||
| including any wrappers (such as those created by `.once`. |
There was a problem hiding this comment.
Missing end parenthesis. Also, maybe link once()?
Sorry, something went wrong.
| --> | ||
| - `eventName` {any} | ||
|
|
||
| Returns a copy of the array of listeners for the event named `eventName`, |
There was a problem hiding this comment.
I think saying 'event named' sounds a little confusing. Maybe something like 'event in' instead? Or maybe just 'event'?
Sorry, something went wrong.
There was a problem hiding this comment.
I can change this but we have another 9 instances of this exact phrasing in this doc.
Sorry, something went wrong.
There was a problem hiding this comment.
I think the current wording is fine here and if we do change it, I'd rather do that throughout the doc and in a separate PR for consistency
Sorry, something went wrong.
|
|
||
| EventEmitter.prototype.domain = undefined; | ||
| EventEmitter.prototype._events = undefined; | ||
| EventEmitter.prototype._eventsCount = 0; |
There was a problem hiding this comment.
You might check the performance on this, but I remember at least back with Crankshaft, putting properties like this (that are likely to change in every EventEmitter instance) on the prototype could slow things down.
Sorry, something went wrong.
There was a problem hiding this comment.
It's definitely fine now from the testing I did. (I ran benchmarks with & without.)
Sorry, something went wrong.
| } | ||
|
|
||
| // Check for listener leak | ||
| const m = $getMaxListeners(target); |
There was a problem hiding this comment.
Did you benchmark this particular change? I would think that checking list.warned is less expensive than calling out to $getMaxListeners() every time, when .warned === true?
Sorry, something went wrong.
There was a problem hiding this comment.
Accessing .warned or any other named key on an Array is really expensive from the testing I've done. Also, even if it wasn't, it's better to optimize for the case where warned === false since that's going to be more common.
(It's been a while but as I can recall, this change alone made roughly 2% difference for cases where warned === false. This was before I added all the major changes that account for the 20-120% difference.)
Sorry, something went wrong.
|
@addaleax & @TimothyGu would you consider reviewing this please? You had some feedback for the previous iteration of this in #17074 Thanks! |
Sorry, something went wrong.
There was a problem hiding this comment.
It might take a bit to review this but I’ll try to get to it :)
Sorry, something went wrong.
|
|
||
| Returns a reference to the `EventEmitter`, so that calls can be chained. | ||
|
|
||
| ### emitter.wrappedListeners(eventName) |
There was a problem hiding this comment.
I kind of feel like something like rawListeners() might be a better name, but this should be okay.
(i.e. I’m not saying it is a better name, just that wrappedListeners() does not immediately tell you what this does … neither does rawListeners(), obviously, but I think it comes closer? 😄 )
Sorry, something went wrong.
There was a problem hiding this comment.
Changes to lib, src, test LGTM
Sorry, something went wrong.
| --> | ||
| - `eventName` {any} | ||
|
|
||
| Returns a copy of the array of listeners for the event named `eventName`, |
There was a problem hiding this comment.
I think the current wording is fine here and if we do change it, I'd rather do that throughout the doc and in a separate PR for consistency
Sorry, something went wrong.
| if (position === 0) | ||
| if (list.length === 2) | ||
| events[type] = list[position ? 0 : 1]; | ||
| else if (list.emitting) { |
There was a problem hiding this comment.
Tiny nit: Can you add {} for the if as well if there is one for the else part as well?
Sorry, something went wrong.
|
|
||
| EventEmitter.prototype.listeners = function listeners(type) { | ||
| const events = this._events; | ||
| function _listeners(target, type, unwrap) { |
There was a problem hiding this comment.
I wonder if it would make sense to turn this into a closure where unwrap can be constant-folded, like:
function makeListeners(unwrap) {
return function listeners(type) {
// Code of _listeners with `this` as `target`
}
}
EventEmitter.prototype.listeners = makeListeners(true);
EventEmitter.prototype.rawListeners = makeListeners(false);
Sorry, something went wrong.
There was a problem hiding this comment.
This was just to mimic how we do it elsewhere in this file, namely addListener and prependListener but yeah, this could also work. I suppose the suggested version does close over one variable whereas what I'm guessing happens with the current implementation is that V8 inlines _listeners?
No clue what's preferable. Maybe just stick with consistency for now?
Sorry, something went wrong.
There was a problem hiding this comment.
If there is no perf difference, then yes, just stick with this :)
Sorry, something went wrong.
| } | ||
|
|
||
| // We must have Symbols to fill in | ||
| if (j < count) { |
There was a problem hiding this comment.
Tiny nit: It would make sense to me if the comment was inside the if since it refers to what must be true if the condition is fulfilled... does that make sense?
Sorry, something went wrong.
| } | ||
| } else if (typeof list !== 'function') { | ||
| position = -1; | ||
| events[type] = undefined; |
There was a problem hiding this comment.
This will cause memory leaks.
Sorry, something went wrong.
There was a problem hiding this comment.
Yep, I originally ran a modified version of our ee-add-remove benchmark but the 25e4 iterations are not very helpful in testing this. It also regresses the performance once the size of the dictionary object grows (although that seems to not be an issue in later versions of V8, the memory still is).
Mostly I think I was just too optimistic re: user input into the EventEmitter being mostly constant. Probably not a safe assumption.
Sorry, something went wrong.
| this._events = Object.create(null); | ||
| else | ||
| delete events[type]; | ||
| events[type] = undefined; |
There was a problem hiding this comment.
This will also cause memory leaks.
Sorry, something went wrong.
At its call sites, `ClearFatalExceptionHandlers()` was used to make the process crash as soon as possible once an exception occurred, without giving JS land a chance to interfere. `ClearFatalExceptionHandlers()` awkwardly removed the current domain and any `uncaughtException` handlers, whereas a clearer way is to execute the relevant reporting (and `exit()`) code directly. PR-URL: #17333 Refs: #17159 Refs: #17324 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Andreas Madsen <amwebdk@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
At its call sites, `ClearFatalExceptionHandlers()` was used to make the process crash as soon as possible once an exception occurred, without giving JS land a chance to interfere. `ClearFatalExceptionHandlers()` awkwardly removed the current domain and any `uncaughtException` handlers, whereas a clearer way is to execute the relevant reporting (and `exit()`) code directly. PR-URL: #17333 Refs: #17159 Refs: #17324 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Andreas Madsen <amwebdk@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
| Back | FazBrowse Home | New Git URL |
This is a bit broad in scope but all related so hard to separate into independent PRs. There are two commits though to make reviewing easier.
1st commit:
2nd commit:
CitGM came back clean for these changes. Also, here are the results from the Benchmark CI:
Refs: #17074
Checklist
Affected core subsystem(s)
domain, events, process