Skip to content
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: fire close event from stdio #22892

Closed
wants to merge 1 commit into from

Conversation

koh110
Copy link
Contributor

@koh110 koh110 commented Sep 17, 2018

Event of close does not fire when giving for stdio option that type is wrap.
For example, if we set event listener to net.server and giving child_process, that events never fire.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@nodejs-github-bot nodejs-github-bot added the child_process Issues and PRs related to the child_process subsystem. label Sep 17, 2018
@@ -379,6 +379,9 @@ ChildProcess.prototype.spawn = function(options) {

if (i > 0 && this.pid !== 0) {
this._closesNeeded++;
if (stream._events && stream._events.close) {
stream.socket.addListener('close', stream._events.close);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would lead to the event handler being called with a wrong this value, and I think this might not work if there is more than one listener (because stream._events.close would be an array then).

Could we use the listener in the lines below, and call stream.emit('close') from there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. It might not work when events is an array.

I want to fix.
createSocket create new Socket object and sharing only handle of old socket.
Therefore, we can't call old close events.
How do i fix it best ?

https://github.com/nodejs/node/blob/5a3e445304f3920590b2ae1d1237fe7615572a44/lib/internal/child_process.js#L274
https://github.com/nodejs/node/blob/5a3e445304f3920590b2ae1d1237fe7615572a44/lib/internal/child_process.js#L377

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Therefore, we can't call old close events.

I’m not sure I understand this – Can you explain more? i.e. what wouldn’t work if we add stream.emit('close'); in the callback below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was misunderstanding. It could use stream.emit('close'), if new stream object have old stream object instead of having _events.

I am going to fix.

if (stream._stdio) {
  stream._stdio.emit('close');
}
  handle: handle,
- _events: events 
+ _stdio: stdio

@koh110
Copy link
Contributor Author

koh110 commented Sep 21, 2018

@addaleax Hi! I fixed this code. Can you review it ?

@@ -379,6 +379,9 @@ ChildProcess.prototype.spawn = function(options) {

if (i > 0 && this.pid !== 0) {
this._closesNeeded++;
if (stream._stdio) {
stream._stdio.emit('close');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t this go in the stream.socket.on('close', () => { handler below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@addaleax I could do it. I fixed and push this code. Please check it.

Copy link
Member

@addaleax addaleax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but we probably need to address one thing before this can finally be merged :)

@@ -926,7 +929,8 @@ function _validateStdio(stdio, sync) {
acc.push({
type: 'wrap',
wrapType: getHandleWrapType(handle),
handle: handle
handle: handle,
_stdio: stdio
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So … the code above (https://github.com/nodejs/node/pull/22892/files#diff-f45eb699237c2e38dc9b49b588933c11R923) reads like stdio could be:

  • A native stream handle
  • An object with a handle property which is a native stream handle
  • An object with a _handle property which is a native stream handle

The path that is typically taken for e.g. net.Sockets would be the third one, I think. We should probably look into deprecating at least the first two, but … all that’s a different story and not really for this PR.

What matters is that stdio does not need to be any kind of particular object; most of the time, it’s a net.Socket or similar, but it could also just be { handle: someNativeHandle }. Even though that’s a bad idea imo, the code above points to this being a possibility. This means that the stream._stdio.emit() call above might fail, because emit() might not be available?

I guess a simple solution would be to turn the if (stream._stdio) { conditional into if (stream._stdio && typeof stream._stdio.emit === 'function') { ?

Copy link
Contributor Author

@koh110 koh110 Sep 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I will fix other PR.

I came up with a way when object is pushed. How about this?
Should it be judged when emit()?

const a = {
  type: 'wrap',
  wrapType: getHandleWrapType(handle),
  handle: handle
};

if (typeof stdio.emit === 'function') {
  a._stdio = stdio;
}

acc.push(a);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or how about if (stream._stdio instanceof EventEmitter)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@koh110 I’m not sure. instanceof EventEmitter is probably fine, too?

I would prefer for the _stdio property to be present consistently, though – either it’s always there or always missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@addaleax Yeah, you right. I completely agree. I was wrong.

I would prefer for the _stdio property to be present consistently,

I feel that if (stream._stdio && stream._stdio instanceof EventEmitter) { is more meaningful than if(stream._stdio && typeof stream._stdio.emit === 'function') {. EventEmitter may be changed to WritableStream. What do you think about it?

@koh110
Copy link
Contributor Author

koh110 commented Sep 30, 2018

@addaleax I fixed it. Check please.

@koh110
Copy link
Contributor Author

koh110 commented Oct 19, 2018

@nodejs/child_process It needs another approval. Please review it.

@koh110
Copy link
Contributor Author

koh110 commented Nov 12, 2018

Anyone else approve ?

@Trott
Copy link
Member

Trott commented Nov 14, 2018

@nodejs/child_process Can someone review this?

@Trott
Copy link
Member

Trott commented Nov 15, 2018

(Also: @addaleax Can you confirm this LGTY still?)

@addaleax addaleax added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Nov 24, 2018
@addaleax
Copy link
Member

@Trott
Copy link
Member

Trott commented Nov 25, 2018

Test fails on Windows in CI:

00:36:32 not ok 64 parallel/test-child-process-server-close
00:36:32   ---
00:36:32   duration_ms: 0.152
00:36:32   severity: fail
00:36:32   exitcode: 1
00:36:32   stack: |-
00:36:32     internal/child_process.js:372
00:36:32         throw errnoException(err, 'spawn');
00:36:32         ^
00:36:32     
00:36:32     Error: spawn ENOTSUP
00:36:32         at ChildProcess.spawn (internal/child_process.js:372:11)
00:36:32         at spawn (child_process.js:543:9)
00:36:32         at Server.net.createServer (c:\workspace\node-test-binary-windows\test\parallel\test-child-process-server-close.js:12:3)
00:36:32         at Server.emit (events.js:189:13)
00:36:32         at TCP.onconnection (net.js:1507:8)
00:36:32   ...

@Trott Trott removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Nov 25, 2018
Trott
Trott previously requested changes Nov 25, 2018
Copy link
Member

@Trott Trott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test not passing on Windows...

@koh110
Copy link
Contributor Author

koh110 commented Nov 28, 2018

@Trott I fixed test. I think dir command might be too heavy on Windows.

Travis is failed, but it is only API rate limit. I passed this lint in local.

++node -p 'JSON.parse(process.argv[1])[0].url' '{
  "message": "API rate limit exceeded for 35.239.89.243. (But here'\''s the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
  "documentation_url": "https://developer.github.com/v3/#rate-limiting"
}'

Would you execute CI?

@Trott
Copy link
Member

Trott commented Nov 30, 2018

@koh110
Copy link
Contributor Author

koh110 commented Jan 7, 2019

@Trott I fixed fluky test. I think server would close before client.
Would you execute CI?

@Trott
Copy link
Member

Trott commented Jan 7, 2019

@koh110
Copy link
Contributor Author

koh110 commented Jan 8, 2019

@Trott I am sorry for taking up so much of your time. Please execute CI.

I found out common.mustCall should not be used in data event.

Sometimes this code returns v10.0.0 and \n.

spawn(process.execPath, ['-v'], {
  stdio: ['ignore', conn, 'ignore']
});

I removed common.mustCall from handler of data event. I pray for passing tests 🙏

@gireeshpunathil
Copy link
Member

@koh110
Copy link
Contributor Author

koh110 commented Jan 16, 2019

some test not passing but i think that it unconnected this fix.
Would it be good to do anything?

@Trott
Copy link
Member

Trott commented Jan 17, 2019

@koh110
Copy link
Contributor Author

koh110 commented Jan 17, 2019

Why does it throw ENOTSUP on windows... 😢
it seems windows couldn't do node -v just vs2017.

@koh110
Copy link
Contributor Author

koh110 commented Mar 1, 2019

Windows accept only UV_TTY and UV_NAMED_PIPE...

if (stream->type == UV_TTY) {
stream_handle = ((uv_tty_t*) stream)->handle;
crt_flags = FOPEN | FDEV;
} else if (stream->type == UV_NAMED_PIPE &&
stream->flags & UV_HANDLE_CONNECTION) {
stream_handle = ((uv_pipe_t*) stream)->handle;
crt_flags = FOPEN | FPIPE;
} else {
stream_handle = INVALID_HANDLE_VALUE;
crt_flags = 0;
}

@koh110
Copy link
Contributor Author

koh110 commented Mar 1, 2019

Whould stream->type be checked before spawn?

@Trott
Copy link
Member

Trott commented Mar 1, 2019

Maybe someone on @nodejs/platform-windows can help out or at least give a nudge in the right direction?

@bzoz
Copy link
Contributor

bzoz commented Mar 4, 2019

Passing sockets as stdio handles is not supported by libuv under Windows. The internet says it is possible, but that would require some nontrivial changes to libuv.

Maybe the test can use named pipes?

@koh110
Copy link
Contributor Author

koh110 commented Mar 7, 2019

Yes! Named pipes seems working!! Thank you so much! @Trott @bzoz
I will add document Passing sockets is not supported Windows with other PR. And I will challenge to commit libuv if I can.

This commit skipped over my code. So, i modified a few line. Could you review and CI again? @addaleax

@addaleax
Copy link
Member

addaleax commented Mar 7, 2019

CI: https://ci.nodejs.org/job/node-test-pull-request/21302/

@Trott Can your request-changes be dismissed if CI is green?

@Trott
Copy link
Member

Trott commented Mar 7, 2019

@Trott Can your request-changes be dismissed if CI is green?

Yes.

@addaleax
Copy link
Member

addaleax commented Mar 7, 2019

Landed in 1133e0b, thanks for the PR! 🎉

@addaleax addaleax closed this Mar 7, 2019
addaleax pushed a commit that referenced this pull request Mar 7, 2019
@koh110 koh110 deleted the close_event branch March 12, 2019 10:27
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Mar 12, 2019
BridgeAR pushed a commit that referenced this pull request Mar 14, 2019
danbev pushed a commit that referenced this pull request Mar 19, 2019
PR-URL: #26604
Refs: #22892 (comment)
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Bartosz Sosnowski <[email protected]>
targos pushed a commit to targos/node that referenced this pull request Mar 27, 2019
PR-URL: nodejs#26604
Refs: nodejs#22892 (comment)
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Bartosz Sosnowski <[email protected]>
targos pushed a commit that referenced this pull request Mar 27, 2019
PR-URL: #26604
Refs: #22892 (comment)
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Bartosz Sosnowski <[email protected]>
BethGriggs pushed a commit that referenced this pull request Apr 17, 2019
PR-URL: #26604
Refs: #22892 (comment)
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Bartosz Sosnowski <[email protected]>
MylesBorins pushed a commit that referenced this pull request May 16, 2019
PR-URL: #26604
Refs: #22892 (comment)
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Bartosz Sosnowski <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
child_process Issues and PRs related to the child_process subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants