-
-
Notifications
You must be signed in to change notification settings - Fork 230
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
Stop reading from stdin after programmatic API finishes #253
Stop reading from stdin after programmatic API finishes #253
Conversation
@kimmobrunfeldt It seems like Node 8 reached EOL on Jan 1. Can we remove Node 8 from the CI pipeline? Apparently |
Hey @brandonchinn178 👋 |
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.
Hey! I'm sorry that I took this long to look at your PR.
Your analysis is very good, and it may be unveiling the reason for some other issues reported in that the processes hang forever!
src/flow-control/input-handler.js
Outdated
onFinish() { | ||
if (this.inputStream && this.pauseInputStreamOnFinish) { | ||
// https://github.com/kimmobrunfeldt/concurrently/issues/252 | ||
this.inputStream.pause(); |
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.
Rather than pausing, WDYT if this called .unref()
instead?
We can then get rid of one the pauseInputStreamOnFinish
option.
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.
unref
is only a function on net.Socket
, but not readable Stream, so it doesn't work if stdin
is a file.
$ concurrently ... < foo
(node:51227) UnhandledPromiseRejectionWarning: TypeError: process.stdin.unref is not a function
...
We could update the docs to say "inputStream
is either net.Socket
or readable Stream" and then have this clean up check if the unref
function exists and calls that if it does, but I think that adds too much complexity. 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.
Ah yes, of course it couldn't be this simple 🤦 OK. Need to think a bit 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.
Hmm, I think we could be smart and pause the input stream if it has no more listeners and it either
- was paused before; or
- was not flowing before.
Although, since I haven't been able to give much attention to concurrently, I'd say just introducing the option is probably best.
WDYT?
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 been so long since I worked on this PR, honestly, I can't provide any more insights. (it seems like you are similarly distracted as well) If you're suggesting merging the PR as-is, that would be preferable by me. Are there any more blocking concerns you'd like me to address first?
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.
Just the handler interface, as per #253 (comment)
src/flow-control/base-handler.js
Outdated
/** | ||
* A hook called when all commands have finished (either successful or not). | ||
*/ | ||
onFinish() {} |
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.
WDYT if handle
returned an optional cleanup function, along with the mapped commands?
Having an onFinish
method kinda implies that the instance is single use.
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.
Personally, I like this better:
- It keeps
handle
as a pure transformationCommands => Commands
- It makes the
reduce
logic a bit more complicated in the main function - It might also be a breaking change, if any third-party APIs write their own handlers
- The only way to not make it a breaking change would be to allow
handle
to return eitherCommands
or{ commands, cleanup }
, which is additional complexity for the caller to handle
- The only way to not make it a breaking change would be to allow
But it's your call
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.
For this one I think it's better to introduce a breaking change.
As you correctly stated, third parties can pass their own handlers - and it could become a pain point to manage state across multiple runs of the same instance.
Some other uses I can see for this cleanup function:
- Removing the RxJS subscription (which also removes the event from the
inputStream
, though doesn't make the process exit if it'sstdin
) - Removing listeners on signals in the
KillOnSignal
handler (which can also prevent the process from exiting)
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, that makes sense. I can update the handle api if you want. Are there any docs that I'd need to update as well?
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.
Nope, no docs on creating custom handlers to this date 😞
@gustavohenke any progress on this? |
@gustavohenke How's that? |
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.
Looks very good to me! Thanks for doing this 🏆
Fixes #252 by explicitly calling
.pause()
on the input stream after all commands have finished. This seems to be the only way to fix the problem, and there doesn't seem to be a way to reset theprocess.stdin
state completely to the state beforeconcurrently
ran (see NodeJS docs).Refactored some parts of the code in order to make this change more drop-in:
BaseHandler
class that all handlers extend fromonFinish
toBaseHandler
which runs after all commands have finishedFakeHandler
to tests that extendBaseHandler
instead of ad-hoc handler objects inconcurrently.spec.js
Readable
ininput-handler
tests instead ofEventEmitter
The CLI has not changed in this PR, but the programmatic API now has two new options:
handleInput
: an alias for the oldinputStream: process.stdin
. Just thought this would be nice, since 99% of the time,inputStream
is set toprocess.stdin
pauseInputStreamOnFinish
: Some people might have a use-case where they want to read fromprocess.stdin
further after runningconcurrently
programmatically. In this rare instance, they'd have to explicitly set this option tofalse
, or explicitly callprocess.stdin.resume()
afterwards. I can't imagine this being a common use-case, so I think this explicitness is a good trade-off.Repro steps
start.js
as specified in Programmatic usage errors on Ctrl+C when specifying process.stdin #252, except replacerequire('concurrently')
withrequire('.')
. Put this file in the root of the repo.node start.js
+ Ctrl-C should no longer hang / wait for Ctrl-D