[streams] Finalize pull/pullInto behavior (#423)

(Branched from https://github.com/whatwg/streams/issues/379#issuecomment-171823158)

## Current situation

Based on the result of the discussion at https://github.com/whatwg/streams/issues/363, we've introduced pullInto for ReadableByteStream.

The pull and pullInto in the ReadableByteStream were designed to be invoked in response to read() and read(view) call on the stream. It was temporary design.

Now, in the PR https://github.com/whatwg/streams/pull/418, I'm converging ReadableStream and ReadableByteStream. Based on the conclusion on https://github.com/whatwg/streams/issues/353, ReadableByteStream has a queue in it as well as ReadableStream. I've also ported the strategy based queuing control mechanism to ReadableByteStream for converging. So, now the 1:1 correspondence between read()/read(view) and pull/pullInto is impossible.

I'm trying to give a reasonable semantics to them.

----

## Role of pullInto

Currently, pullInto has a role of notifying the underlying byte source of the outstanding TypedArray (and region on it) to which the underlying byte source should write generated data. So, whenever:

- a new TypedArray comes
- or the outstanding TypedArray is partially filled
- or filled the outstanding TypedArray but there're more TypedArrays waiting to get filled

the controller should invoke a pullInto.

Unlike pullInto, the signal of pull is just that "the amount in the queue has changed". This is probable by looking at `desiredSize`.

----

## Plan taken in the PR

My plan was:

- pull is invoked when:
  - read() is called when the queue is empty
  - read() is called and it consumed all or part of the queue
  - read(view) is called and it consumed all or part of the queue
- pullInto is invoked when:
  - read(view) is called when the queue is empty
  - read(view) is called and it consumed part of the queue but needs to fill more bytes to return to the consumer with at least 1 element (e.g. 2 byte for Uint16Array) filled.

I planned to do nothing on invocation of controller methods. For example, after the source calls controller.enqueue() in pull (or outside pull), the source can probe how much more data is required to fill the queue by accessing controller.desiredSize. But I realized it's more complicated. After calling enqueue() in response to pullInto (either in pullInto or async to pullInto), it's possible there's more read(view)s are waiting.

It seems some more redesign should be made.

----

## Invocation timing and repetition

ReadableByteStream has a new pull/pullInto invocation scheme.

- pull is required when the current call stack contains any pull/pullInto (check by `_pulling`), schedule by setting `_pullAgain`
- pull/pullInto invocation code is in (or followed by) a while loop (see `ReadableByteStreamControllerCallPullOrPullIntoRepeatedlyIfNeeded()`) to repeat pull/pullInto invocation synchronously while `_pullAgain` is set in the last pull/pullInto
- when respond() or enqueue() fail to fill enough bytes (only 1 byte filled in Uint16Array), asynchronously invoke pullInto with the remaining region again.
  - this depends on how we resolve the issue above (addition of desiredSize like API?)


---
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/issues/423

Received on Friday, 15 January 2016 10:58:25 UTC