stdio: implement manual start for ReadStream#36277
stdio: implement manual start for ReadStream#36277mmomtchev wants to merge 3 commits intonodejs:masterfrom
Conversation
|
cc @nodejs/streams |
ronag
left a comment
There was a problem hiding this comment.
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? |
mcollina
left a comment
There was a problem hiding this comment.
Could this be implemented outside of Readable? How could somebody start the stream if manualStart is true?
| destroyImpl.construct(this, () => { | ||
| maybeReadMore(this, this._readableState); | ||
| if (!options || !options.manualStart) | ||
| maybeReadMore(this, this._readableState); |
There was a problem hiding this comment.
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.
@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
manualStartoptions as hidden non-documented options, eventually replacing them withSymbolnames - 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);
}
}) |
|
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.
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.
thanks for the clarification. Maybe add this in a comment?
| fd: fd, | ||
| manualStart: true | ||
| }); | ||
| setTimeout(() => assert(rs.bytesRead === 0), common.platformTimeout(10)); |
| const assert = require('assert'); | ||
| const fs = require('fs'); | ||
|
|
||
| fs.promises.open(__filename).then(common.mustCall((fd) => { |
There was a problem hiding this comment.
why are you using fs.promises? Could you please use fs?
There was a problem hiding this comment.
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.
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.
A Readable always starts in paused mode, this change affects only a ReadStream
There was a problem hiding this comment.
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.
A
Readablealways 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.
Maybe there is some biggus problem, how do you think?
There was a problem hiding this comment.
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.
I'm confused what the problem here is? We just need change ReadStream to Readable. What is the "biggus" problem?
|
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