-
Notifications
You must be signed in to change notification settings - Fork 166
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
Readable byte stream must call pull after receiving new chunk #877
Comments
Maybe some background information, in case you're wondering how I ended up here... I'm currently working on an adapter library between native streams (e.g. from a fetch My idea is to implement helper functions that create an underlying source/sink that pulls from/writes to a given (native) readable/writable stream. This way, the created source/sink can be passed to a polyfilled stream constructor, resulting in a polyfilled stream that pulls from/writes to a native stream. Under the hood, the created source/sink implementations request a reader/writer from the original stream and use that to respond to So far, I have a somewhat functional adapter that creates an underlying source pulling from a given readable stream. To verify this adapter, I've created a new
So you end up with something like:
And then I try to get this |
I also think this test should pass. @tyoshino, you're the expert on byte streams. Should this work? |
Hmm, looks like this might be slightly trickier than I initially thought. With my proposed fix, two tests fail:
The first test seems wrong to me. There are two The second test seems like a bug with my fix. Here, there are three EDIT1: if (controller._pullAgain === true && ReadableStreamDefaultControllerShouldCallPull(controller)) {
controller._pullAgain = false;
return ReadableStreamDefaultControllerCallPullIfNeeded(controller);
}
EDIT2: My bad, I've been messing around with the tests too much. I accidentally added a The 3 initial reads can be fulfilled by one Interestingly, since the high water mark is 256, the stream will keep calling EDIT3: Forget about edit 1, |
If I think the correct behaviour may depend on what kind of reader is in use. If the stream is locked to a byob reader then we'd like to wait to see whether a buffer is provided to read into. For a default reader we just want to fill the queue ASAP. I assume this is the reason why the behaviour with a default reader is a little off. @tschneidereit I know you have been looking at the byte stream code recently. What do you think? |
@ricea Ah, I didn't think about BYOB requests. Yes, it'd be more efficient to wait until we know whether the next read comes from a default reader or a BYOB reader. However, wouldn't that imply that a byte stream locked to a BYOB reader will never attempt to fill its queue up to the high water mark? Also, what should happen if the stream isn't locked (yet)? I don't think the stream's buffering should depend on the type of its current reader. If a user really wants all reads from a byte stream to be as efficient as possible, then they can just set the stream's high water mark to 0. That way, the stream will only pull when there are pending read requests. |
@ricea, I'm on PTO and won't be able to spend too much time on digging into this until the week after next at the earliest, I'm afraid. Please don't gate any decisions here on my feedback if you want things to progress earlier than that. |
@tschneidereit Don't worry about it. I'm happy to take the time to fix this properly. |
I'm concerned that this may leave the creators of readable byte streams in the uncomfortable position of having to choose between optimal behaviour for byob readers or optimal behaviour for default readers. However, I agree that depending on the type of the current reader is too problematic. Do you think you can get "always pull() as long as the queue has space and progress is happening" semantics working? Even if it's not working, it would be helpful to see a work-in-progress that I could work from. |
The proposed fix has those semantics, that's exactly what I feel like these should be the desired semantics. It also matches with the proposed detailed description for the underlying source API in #875:
|
@MattiasBuelens are you still interested in a PR for fixing this? |
@domenic Sure, I'll submit a PR with my proposed solution. I thought I already did, but I guess I forgot. 😛 |
After a readable byte stream receives a new chunk from the underlying source, the stream must call pull() (if needed). Otherwise, the stream may get stuck. Add calls to ReadableByteStreamControllerCallPullIfNeeded at the end of ReadableByteStreamControllerEnqueue and ReadableByteStreamControllerRespondInternal to ensure this. Fixes #877.
After a readable byte stream receives a new chunk from the underlying source, the stream must call
pull
(if needed). Otherwise, the stream may get stuck (see test below).For regular streams, this is already done correctly:
ReadableStreamDefaultController
correctly callsReadableStreamDefaultControllerCallPullIfNeeded
. However, byte streams do not call the correspondingReadableByteStreamControllerCallPullIfNeeded
in response toenqueue
,respond
orrespondWith
.Test
The following test (written as a Web platform test) demonstrates the problem. Here, a readable byte stream is constructed that will enqueue a new chunk every time
pull
is called, and closes the stream after 3 chunks. (This test is derived from the existing test "ReadableStream with byte source: Respond to pull() by enqueue() asynchronously", but enqueues its chunks in separate pulls instead.)This test times out with the current reference implementation. The first read resolves, but the other reads remain pending.
pull
is only called 1 time, rather than the expected 4 times (3 times to enqueue a chunk, 1 last time to close the source).However, if the test is changed to use regular readable streams instead of byte streams, the test passes (see highlighted line).
Proposed solution
I believe the fix should be to add calls to
ReadableByteStreamControllerCallPullIfNeeded
at the end of:ReadableByteStreamControllerEnqueue
(handlesenqueue
)ReadableByteStreamControllerRespondInternal
(handlesrespond
andrespondWith
)If you want, I can make a pull request that does just that. The above test can be added directly to the Web platform tests, but I'd also need to make similar tests for
respond
andrespondWith
.The text was updated successfully, but these errors were encountered: