-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
child_process: support Symbol.dispose
#48551
Conversation
Symbol.asyncDestroy
Symbol.asyncDispose
eb3ccaa
to
50f076d
Compare
lib/internal/child_process.js
Outdated
@@ -516,6 +520,15 @@ ChildProcess.prototype.kill = function(sig) { | |||
return false; | |||
}; | |||
|
|||
ChildProcess.prototype[SymbolAsyncDispose] = function() { | |||
return new Promise((resolve) => { | |||
this.on('exit', () => resolve(null)); |
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.
What if already exited?
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.
What if the user remove all exit
event handlers?
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.
Isn't this changing a little too much? I think this PR could be more minimal.
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'm unsure whether it's sufficient to wait for 'exit'
or 'close'
is the more correct event to wait for? Not sure exactly what the difference is between these two.
Let's add a ChildProcess.prototype[SymbolAsyncDispose] = async function() {
if (!this.closed) {
await EE.once(this, 'close');
}
}; |
according to the code, |
we cannot use |
de376ce
to
034c37a
Compare
lib/internal/child_process.js
Outdated
const ret = this.kill(this[kKillSignal]); | ||
assert(ret, 'unexpected kill failure'); | ||
await EventEmitter.once(this, 'close'); | ||
this.emit('error', new AbortError()); |
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.
Why this? Not saying I disagree. Just not sure whether I do agree. @benjamingr any thoughts?
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.
So other consumers of the child process know it was aborted. this is the same behavior we've implemented on Readable[Symbol.asyncDispose]
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 is a race here though. The process might still exit successfully.
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 am Not sure I follow. can you provide an example?
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.
The process can succeed before it receives the kill. In which case you are emitting a bad error.
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.
An abortion is just a hint. The process may still choose to fully complete.
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 this quickly becomes complicated. I'd prefer not to emit any error here.
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 can be a surprising behavior for a child process to exit without a kill signal and a code, and with incomplete stdout/stderr
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.
Let's ask for @ronbuckton 's advice:
The question here is:
await using cp = childProcess1;
// someOtherCode
doSomethingWith(childProcess1);
What should the behavior be from doSomethingWith's side? For example if someone is reading stdout while another consumer is disposing it should it error?
Any signal other than SIGKILL may actually be ignored by the process. Which would lead to a deadlock in our case. |
a pending promise is not a deadlock |
It is if you wait for it, expecting it to resolve and it never does |
I'd like to have some more opinions on this. I feel there is a risk of this being an anti-pattern that we encourage. |
Maybe we can get around the deadlock issue by always having a timeout which eventually calls sigkill and makes sure the promise will eventually resolve. |
An alternative is to implement |
I think this is the only reasonable option given we don't know how to wait for the process to terminate "for sure" |
0b342e8
to
5d2af02
Compare
Symbol.asyncDispose
Symbol.dispose
Landed in 773fde2 |
PR-URL: #48551 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: nodejs#48551 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: nodejs#48551 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: #48551 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: #48551 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: #48551 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
No description provided.