-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
domain: add arguments for the function in domain.run() #15
Conversation
@@ -196,7 +196,8 @@ Domain.prototype.run = function(fn) { | |||
if (this._disposed) | |||
return; | |||
this.enter(); | |||
var ret = fn.call(this); | |||
var args = Array.prototype.slice.call(arguments, 1); |
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.
fyi this is unnecessary, just use arguments
in the fn.apply()
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 looks correct to me. It needs to slice off the fn argument, doesn't it?
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.
@rvagg, what @bnoordhuis says is correct, the function needs all the arguments but the first, which is the function itself.
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.
Sorry, didn't notice the 1
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.
Shouldn't it be the same as https://github.com/iojs/io.js/blob/v0.12/lib/events.js#L103 ?
LGTM FWIW. |
I suppose one question that needs answering is whether @micnic Can you make the commit log conform to the template as described in CONTRIBUTING.md? Thanks. |
@bnoordhuis I agree. Also it's possible just to call |
@bnoordhuis, I'll add a switch for different cases as @vkurchatkin proposes. related to the commit log, should I make a new pull request or just another commit with a more verbose description? (it's my first PR) |
@micnic That's a good idea but can you then also add a small benchmark in benchmarks/ that shows that the switch statement actually speeds things up? Thanks. As to the commit log, you can just |
@bnoordhuis, I added the benchmark and made some changes to the arguments slicing logic to: if (arguments.length >= 2) {
args = Array.prototype.slice.call(arguments, 1);
ret = fn.apply(this, args);
} else {
ret = fn.call(this);
} it's similar to what is used in Now, what I discovered is that we also should bind the arguments to the domain if they may emit any errors, take in count this use case: var domain = require('domain');
var events = require('events');
var dom = domain.create();
var emitter = new events.EventEmitter();
dom.on('error', function (error) {
console.log(error.message + ' is caught');
});
dom.run(function (ee) {
ee.emit('someEvent');
}, emitter);
// This error is not caught by the domain
emitter.emit('error', new Error('Oh No!')); What do you think about explicitly binding the arguments to the domain? |
sounds counterintuitive and probably will cause lots of confusion |
d7e65ff
to
185d11c
Compare
Hello! I am pleased to see your valuable contribution to this project. Would you Questions:
Please provide the answers in an ordered list like this:
Note that I am just a bot with a limited human-reply parsing abilities, In case of success I will say: In case of validation problem I will say: Truly yours, Responsibilities
|
|
...summoning the core team devs! |
I think this conflicts with #66. I'll bring it up in the TC meeting today; if we are going to deprecate domains, it doesn't make sense to extend the API. |
this.enter(); | ||
var ret = fn.call(this); | ||
if (arguments.length >= 2) { | ||
args = Array.prototype.slice.call(arguments, 1); |
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.
I did some quick research and calling Array.prototype.slice.call(arguments)
always deopts the function. You can see it for yourself when you run with --trace_opt --trace_deopt
; V8 always bails out with "Bad value context for arguments value". It's unavoidable and probably doesn't matter much in the grand scheme of things but it's still a pity.
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.
I believe you can work around this by copying arguments
to args
using a loop. Not sure if it's worth it in this case.
Ref: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
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.
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.
This is a bit of a fun one because slicing the arguments manually is not usually faster than calling Array::slice()
- so whether this is worthwhile or not depends on how much work the rest of your function does. In this case that function isn't doing a lot of work itself but it means that V8 can't inline the function calls. This is a slightly obfuscated version that should be faster:
Domain.prototype.run = function(fn) {
if (this._disposed)
return;
if (arguments.length >= 2) {
return this._runWithArgs(fn, Array.prototype.slice.call(arguments, 1));
} else {
return this._runNaked(fn);
}
};
Domain.prototype._runNaked = function (fn) {
this.enter();
var ret = fn.call(this);
this.exit();
return ret;
};
Domain.prototype._runWithArgs = function (fn, args) {
this.enter();
var ret = fn.apply(this, args);
this.exit();
return ret;
};
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.
This section of code from lib/events.js
is probably the correct approach to take - https://github.com/iojs/io.js/blob/v0.12/lib/events.js#L110-L127
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.
I think we should have a standard function to handle the arguments in the most optimal way, something like this:
util.getArgumentsAsArray = function (args, slice) {
var length = args.length - slice;
var arr = new Array(length);
var index = slice;
while (index < length) {
arr[index - slice] = args[index];
index++;
}
return arr;
};
// or maybe ES6 Array.from() not sure about it
Array.from(arguments).slice(1);
This function may be used in setTimeout()
, setInterval()
, setImmediate()
, EventEmitter.emit()
, domain.run()
, in some future implementation with custom arguments and in third-party modules.
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.
@micnic how would you call this function without leaking the arguments
from the caller?
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.
My understanding is that arguments
are just a pain to deal with and that passing them to a function, like your getArgumentsAsArray()
, is enough to cause a deoptimization in most cases.
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.
There's no way to slice or otherwise manipulate arguments
without doing it manually if you want to avoid deopts. The only function that it is ok to pass a naked arguments
to is Function.prototype.apply
as it is special cased. Bluebird uses a macro to make this more convenient, also see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments for more info.
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.
@phpnode, ah, you are right, there is no way no avoid deoptimization, thanks for clarifying, I hope V8 devs will improve this.
I'll just add the loop manipulation as it is in the EventEmitter.
Adds the feature to define arguments for the function called in domain.run(), this is supposed to be useful when a function is called from another context and some values from the current context are needed as arguments, it's similar to the callback from setTimeout or setInterval.
This PR is ready for merge, I made the demanded changes |
@micnic @chrisdickinson will take it from here, he graciously volunteered to shepherd this PR. |
PR for nodejs/node-v0.x-archive#7193