Uh oh!
There was an error while loading. Please reload this page.
stdio: implement manual start for ReadStream - #36277
Conversation
joyeecheung
commented
Nov 26, 2020
cc @nodejs/streams |
ronag
left a comment
There was a problem hiding this comment.
manualStart should be documented.
ronag
commented
Nov 26, 2020
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. |
mscdex
commented
Nov 26, 2020
Minor nit: perhaps call this |
mmomtchev
commented
Nov 26, 2020
@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
Uh oh!
There was an error while loading. Please reload this page.
ronag
commented
Nov 27, 2020
What about? constmyStream=createStream()myStream.pause() |
mmomtchev
commented
Nov 27, 2020
@ronag For a
|
mmomtchev
commented
Nov 27, 2020
Wait a second, just tested it and maybe you are right |
mmomtchev
commented
Nov 27, 2020
@ronag, yes, in fact the |
ronag
commented
Nov 27, 2020
Maybe we should fix this? i.e. maybeReadMore does nothing if paused. |
mmomtchev
commented
Nov 27, 2020
Look at this: node/lib/internal/streams/readable.js Line 620 in 0a23d65 If the stream is paused, So using constmyStream=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);}}) |
nodejs-github-bot
commented
Nov 27, 2020
mmomtchev
commented
Nov 27, 2020
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 https://github.com/nodejs/node/blob/master/test/parallel/test-stream-readable-hwm-0-no-flow-data.js |
When binding |
mmomtchev
commented
Nov 27, 2020
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 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ronag
commented
Nov 27, 2020
Maybe: diff--gita/lib/internal/streams/readable.jsb/lib/internal/streams/readable.jsindex93153908fe..4f72c0cdc1100644---a/lib/internal/streams/readable.js+++b/lib/internal/streams/readable.js
@@ -196,7+196,9 @@ functionReadable(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){}elseif(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); |
mmomtchev
commented
Dec 1, 2020
@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? |
mmomtchev
commented
Dec 2, 2020
In that unit test, once you pause it because of the |
mmomtchev
commented
Dec 9, 2020
@ronag, I can't get |
7fc79f0 to
5cd8cb6Compare| 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?
mmomtchev
commented
Jan 13, 2021
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