-
Notifications
You must be signed in to change notification settings - Fork 30k
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
stdio: implement manual start for ReadStream #36277
Conversation
cc @nodejs/streams |
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.
manualStart
should be documented.
I'm not sure I understand the root problem here. When would it ever not be safe to start reading immediately? Seems more like a workaround for a different issue. |
Minor nit: perhaps call this |
@ronag @mscdex there is already a Where should this be documented? |
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.
Could this be implemented outside of Readable
? How could somebody start the stream if manualStart
is true
?
@@ -196,7 +196,8 @@ function Readable(options) { | |||
Stream.call(this, options); | |||
|
|||
destroyImpl.construct(this, () => { | |||
maybeReadMore(this, this._readableState); | |||
if (!options || !options.manualStart) | |||
maybeReadMore(this, this._readableState); |
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 needs some tests that are specific to streams. How is a user of Stream be able to use this?
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.
@mcollina @joyeecheung @ronag @mscdex
Attaching a data
handler will start the stream.
I simply recreated the semantics of the non-documented option in net.Socket
- which is what the thread bootstrap code uses.
Its only user at the moment is this thread bootstrap code for the stdin
stream. The net.Socket
option has a second use case in TLSWrap
- but I am not sure if it actually works - because this wouldn't happen : #35475
The reason I did in Readable
is simply that the reading is started from here for all Readable
except net.Socket
which has its own startup code
To move this to ReadStream
I would need to reimplement part of this code and it will be a much more invasive change
So two options:
- Keep both
manualStart
options as hidden non-documented options, eventually replacing them withSymbol
names - Document them, unit test them and make them official
It is also worth noting that there are two other net.Socket
specific options, both non-documented, pauseOnStart
and pauseOnConnect
- which have different semantics - the bootstrap code being made for manualStart
What about? const myStream = createStream()
myStream.pause() |
@ronag For a
|
Wait a second, just tested it and maybe you are right |
@ronag, yes, in fact the |
Maybe we should fix this? i.e. maybeReadMore does nothing if paused. |
Look at this: node/lib/internal/streams/readable.js Line 620 in 0a23d65
If the stream is paused, So using const myStream = createStream()
myStream._readableState.highWaterMark = 0;
myStream.pause() The above solution also works, I tested it Is this intended? It does not feel very right |
@mmomtchev maybe in the destroyImpl.construct(this, () => {
if (!this.isPaused()) {
maybeReadMore(this, this._readableState);
}
}) |
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.
lgtm
This test node/lib/internal/streams/readable.js Line 873 in 0a23d65
Does anyone know why |
I tried removing it and there is an unit test which does specifically test for this, although I suspect that it might not be intentional |
When binding |
Look at this test: https://github.com/nodejs/node/blob/master/test/parallel/test-stream-preprocess.js This works because the stream continues reading - otherwise the |
Maybe: diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js
index 93153908fe..4f72c0cdc1 100644
--- a/lib/internal/streams/readable.js
+++ b/lib/internal/streams/readable.js
@@ -196,7 +196,9 @@ function Readable(options) {
Stream.call(this, options);
destroyImpl.construct(this, () => {
- maybeReadMore(this, this._readableState);
+ if (!this.isPaused()) {
+ maybeReadMore(this, this._readableState);
+ }
});
}
@@ -870,8 +872,8 @@ Readable.prototype.on = function(ev, fn) {
} else if (ev === 'readable') {
if (!state.endEmitted && !state.readableListening) {
state.readableListening = state.needReadable = true;
- state.flowing = false;
+ this.pause();
state.emittedReadable = false;
debug('on readable', state.length, state.reading);
if (state.length) {
emitReadable(this); |
@ronag, this is a bold move and I like it because it is a move in the right direction - having only one paused flag with uniform semantics, but I will have to modify this unit test and there will probably be user code that won't run after this change |
What exactly fails with my proposal? |
In that unit test, once you pause it because of the |
@ronag, I can't get |
7fc79f0
to
5cd8cb6
Compare
fd: fd, | ||
manualStart: false | ||
}); | ||
setTimeout(() => assert(rs.bytesRead > 0), common.platformTimeout(10)); |
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 should be a setImmediate.
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 read handler is already in a setImmediate
- so this will run the assert before Node has a chance to start reading data.
I don't have any ideas on how to achieve this without a timer.
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.
thanks for the clarification. Maybe add this in a comment?
fd: fd, | ||
manualStart: true | ||
}); | ||
setTimeout(() => assert(rs.bytesRead === 0), common.platformTimeout(10)); |
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.
his should be a setImmediate
const assert = require('assert'); | ||
const fs = require('fs'); | ||
|
||
fs.promises.open(__filename).then(common.mustCall((fd) => { |
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 are you using fs.promises
? Could you please use fs
?
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.
Because a cook who doesn't eat its own food is not a good cook 😄
const fs = require('fs'); | ||
|
||
fs.promises.open(__filename).then(common.mustCall((fd) => { | ||
const rs = new fs.ReadStream(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.
This test fs.ReadStream
, not stream.Readable
. While this test is correct, can you please use one that just use stream.Readable
?
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.
A Readable
always starts in paused mode, this change affects only a ReadStream
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.
A Readable always starts in paused mode
How is that relevant? It behaves the same as ReadStream. The same test should 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.
A
Readable
always starts in paused mode, this change affects only aReadStream
If that was true, you'd not need to modify Readable
.
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.
Maybe there is some biggus problem, how do you think?
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.
Maybe there is some biggus problem, how do you think?
Likely, but it's possible to add an independent test.
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 confused what the problem here is? We just need change ReadStream to Readable. What is the "biggus" problem?
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.
Please see #36823
Included in #36823 |
All stdio ReadStream's use manual start to avoid
consuming data for example when a process
execs/spawns
Refs: #36251
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes